| Merge pull request #4 from EmperorPenguin18/testing - mpv-jellyfin - MPV script… | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| commit 9220ad8195ee3e8f844bc1fb5f205c96c31f3450 | |
| parent 7aef1c8f0dfbf7c68456d03bde28d8f972c20617 | |
| Author: Sebastien MacDougall-Landry <[email protected]… | |
| Date: Thu, 23 May 2024 21:38:51 -0400 | |
| Merge pull request #4 from EmperorPenguin18/testing | |
| Merge changes from testing | |
| Diffstat: | |
| A PKGBUILD | 18 ++++++++++++++++++ | |
| M README.md | 2 -- | |
| M scripts/jellyfin.lua | 193 +++++++++++++++++------------… | |
| 3 files changed, 124 insertions(+), 89 deletions(-) | |
| --- | |
| diff --git a/PKGBUILD b/PKGBUILD | |
| @@ -0,0 +1,18 @@ | |
| +# Maintainer: Sebastien MacDougall-Landry | |
| + | |
| +pkgname=mpv-jellyfin | |
| +pkgver=1.1 | |
| +pkgrel=1 | |
| +pkgdesc='mpv plugin that turns it into a Jellyfin client' | |
| +url='https://github.com/EmperorPenguin18/mpv-jellyfin/' | |
| +source=("$pkgname-$pkgver.tar.gz::https://github.com/EmperorPenguin18/mpv-jell… | |
| +arch=('any') | |
| +license=('Unlicense') | |
| +depends=('mpv' 'curl') | |
| +sha256sums=('') | |
| + | |
| +package () { | |
| + cd "$srcdir/$pkgname-$pkgver" | |
| + install -Dm644 scripts/jellyfin.lua -t "$pkgdir/etc/mpv/scripts" | |
| + install -Dm644 script-opts/jellyfin.conf -t "$pkgdir/etc/mpv/script-opts" | |
| +} | |
| diff --git a/README.md b/README.md | |
| @@ -29,6 +29,4 @@ When you activate a video in the menu, it will begin to play … | |
| In general this is a very minimal script and isn't designed to be a full Jelly… | |
| -Currently navigation is very slow because metadata is pulled in sequentially. … | |
| - | |
| Thumbnails will accumulate if the selected image path isn't tmpfs. In addition… | |
| diff --git a/scripts/jellyfin.lua b/scripts/jellyfin.lua | |
| @@ -16,18 +16,18 @@ local connected = false | |
| local shown = false | |
| local user_id = "" | |
| local api_key = "" | |
| -local library_id = nil | |
| -local library_selection = 1 | |
| -local title_id = nil | |
| -local title_selection = 1 | |
| -local season_id = nil | |
| -local season_selection = 1 | |
| -local video_id = "" | |
| -local selection = 1 | |
| + | |
| +local parent_id = {"", "", ""} | |
| +local selection = {1, 1, 1} | |
| +local layer = 1 | |
| + | |
| local items = {} | |
| local ow, oh, op = 0, 0, 0 | |
| +local video_id = "" | |
| +local async = nil | |
| +local list_start = 1 | |
| -local toggle_overlay | |
| +local toggle_overlay -- function | |
| local function send_request(method, url) | |
| if connected then | |
| @@ -58,82 +58,115 @@ local function line_break(str, flags, space) | |
| return text | |
| end | |
| -local function update_data() | |
| +local function update_list() | |
| overlay.data = "" | |
| - for _, item in ipairs(items) do | |
| - if _ > selection - (53 / op) then | |
| - if _ < selection + (20 * op) then | |
| - if _ == selection then | |
| - overlay.data = overlay.data.."{\\fs16}… | |
| - else | |
| - overlay.data = overlay.data.."{\\fs16}… | |
| - end | |
| - end | |
| + local magic_num = 29 -- const | |
| + if selection[layer] - list_start > magic_num then | |
| + list_start = selection[layer] - magic_num | |
| + elseif selection[layer] - list_start < 0 then | |
| + list_start = selection[layer] | |
| + end | |
| + for i=list_start,list_start+magic_num do | |
| + if i > #items then break end | |
| + local index = "" | |
| + if items[i].IndexNumber and items[i].IsFolder == false then | |
| + index = items[i].IndexNumber..". " | |
| + else | |
| + -- nothing | |
| + end | |
| + if i == selection[layer] then | |
| + overlay.data = overlay.data.."{\\fs16}{\\c&HFF&}"..ind… | |
| + else | |
| + overlay.data = overlay.data.."{\\fs16}"..index..items[… | |
| end | |
| end | |
| overlay:update() | |
| - local id = items[selection].Id | |
| - local width = math.floor(ow/3) | |
| +end | |
| + | |
| +local scale = 2 -- const | |
| + | |
| +local function show_image(success, result, error, userdata) | |
| + if success == true then | |
| + mp.command_native({ | |
| + name = "overlay-add", | |
| + id = 0, | |
| + x = math.floor(ow/2.5), | |
| + y = 10, | |
| + file = userdata[3], | |
| + offset = 0, | |
| + fmt = "bgra", | |
| + w = userdata[1], | |
| + h = userdata[2], | |
| + stride = userdata[1]*4, | |
| + dw = userdata[1]*scale, | |
| + dh = userdata[2]*scale | |
| + }) | |
| + end | |
| +end | |
| + | |
| +local function update_image(item) | |
| + local width = math.floor(ow/(3*scale)) | |
| local height = 0 | |
| local filepath = "" | |
| - for _, image in ipairs(send_request("GET", options.url.."/Items/"..id.… | |
| - if image.ImageType == "Primary" then | |
| - height = math.floor( width*(image.Height / image.Width… | |
| - filepath = options.image_path.."/"..id.."_"..width.."_… | |
| - mp.command_native({ | |
| - name = "subprocess", | |
| - playback_only = false, | |
| - args = { "mpv", options.url.."/Items/"..id.."/… | |
| - }) | |
| - break | |
| - end | |
| - end | |
| mp.commandv("overlay-remove", "0") | |
| - mp.commandv("overlay-add", "0", tostring(math.floor(ow/2.5)), tostring… | |
| + if item.ImageTags.Primary ~= nil then | |
| + height = math.floor(width/item.PrimaryImageAspectRatio) | |
| + filepath = options.image_path.."/"..item.Id.."_"..width.."_"..… | |
| + if async ~= nil then mp.abort_async_command(async) end | |
| + async = mp.command_native_async({ | |
| + name = "subprocess", | |
| + playback_only = false, | |
| + args = { "mpv", options.url.."/Items/"..item.Id.."/Ima… | |
| + }, function(success, result, error) show_image(success, result… | |
| + end | |
| +end | |
| + | |
| +local function update_metadata(item) | |
| meta_overlay.data = "" | |
| - local metadata = send_request("GET", options.url.."/Users/"..user_id..… | |
| - local name = line_break(metadata.Name, "{\\a7}{\\fs24}", 30) | |
| + local name = line_break(item.Name, "{\\a7}{\\fs24}", 30) | |
| meta_overlay.data = meta_overlay.data..name.."\n" | |
| local year = "" | |
| - if metadata.ProductionYear then year = metadata.ProductionYear end | |
| + if item.ProductionYear then year = item.ProductionYear end | |
| local time = "" | |
| - if metadata.RunTimeTicks then time = " "..math.floor(metadata.RunTim… | |
| + if item.RunTimeTicks then time = " "..math.floor(item.RunTimeTicks/6… | |
| local rating = "" | |
| - if metadata.CommunityRating then rating = " "..metadata.CommunityRat… | |
| + if item.CommunityRating then rating = " "..item.CommunityRating end | |
| local hidden = "" | |
| local watched = "" | |
| - if metadata.UserData.Played == false then | |
| + if item.UserData.Played == false then | |
| if options.hide_spoilers ~= "off" then hidden = "{\\bord0}{\\1… | |
| else | |
| watched = " Watched" | |
| end | |
| local favourite = "" | |
| - if metadata.UserData.IsFavorite == true then | |
| + if item.UserData.IsFavorite == true then | |
| favourite = " Favorite" | |
| end | |
| meta_overlay.data = meta_overlay.data.."{\\a7}{\\fs16}"..year..time..r… | |
| - local tagline = line_break(metadata.Taglines[1], "{\\a7}{\\fs20}", 35) | |
| + local tagline = line_break(item.Taglines[1], "{\\a7}{\\fs20}", 35) | |
| meta_overlay.data = meta_overlay.data..tagline.."\n" | |
| - local description = line_break(metadata.Overview, "{\\a7}{\\fs16}"..hi… | |
| + local description = line_break(item.Overview, "{\\a7}{\\fs16}"..hidden… | |
| meta_overlay.data = meta_overlay.data..description | |
| meta_overlay:update() | |
| end | |
| +local function update_data() | |
| + update_list() | |
| + local item = items[selection[layer]] | |
| + update_image(item) | |
| + update_metadata(item) | |
| +end | |
| + | |
| local function update_overlay() | |
| overlay.data = "{\\fs16}Loading..." | |
| overlay:update() | |
| - local result | |
| - if not library_id then | |
| - result = send_request("GET", options.url.."/Items?api_key="..a… | |
| - elseif not title_id then | |
| - result = send_request("GET", options.url.."/Items?api_key="..a… | |
| - elseif not season_id then | |
| - result = send_request("GET", options.url.."/Items?api_key="..a… | |
| + local url = options.url.."/Items?api_key="..api_key.."&userID="..user_… | |
| + if layer == 2 then | |
| + url = url.."&sortBy=SortName" | |
| else | |
| - result = send_request("GET", options.url.."/Items?api_key="..a… | |
| + -- nothing | |
| end | |
| - items = result.Items | |
| - heights = {} | |
| + items = send_request("GET", url).Items | |
| ow, oh, op = mp.get_osd_size() | |
| update_data() | |
| end | |
| @@ -145,54 +178,40 @@ end | |
| local function play_video() | |
| toggle_overlay() | |
| mp.commandv("loadfile", options.url.."/Videos/"..video_id.."/stream?st… | |
| - mp.set_property("force-media-title", items[selection].Name) | |
| + mp.set_property("force-media-title", items[selection[layer]].Name) | |
| end | |
| local function key_up() | |
| - selection = selection - 1 | |
| - if selection == 0 then selection = #items end | |
| - update_data() | |
| + if #items > 1 then | |
| + selection[layer] = selection[layer] - 1 | |
| + if selection[layer] == 0 then selection[layer] = #items end | |
| + update_data() | |
| + end | |
| end | |
| local function key_right() | |
| - if items[selection].IsFolder == false then | |
| - video_id = items[selection].Id | |
| + if items[selection[layer]].IsFolder == false then | |
| + video_id = items[selection[layer]].Id | |
| play_video() | |
| else | |
| - if not library_id then | |
| - library_id = items[selection].Id | |
| - library_selection = selection | |
| - elseif not title_id then | |
| - title_id = items[selection].Id | |
| - title_selection = selection | |
| - elseif not season_id then | |
| - season_id = items[selection].Id | |
| - season_selection = selection | |
| - end | |
| - selection = 1 | |
| + layer = layer + 1 -- shouldn't get too big | |
| + parent_id[layer] = items[selection[layer-1]].Id | |
| + selection[layer] = 1 | |
| update_overlay() | |
| end | |
| end | |
| local function key_down() | |
| - selection = selection + 1 | |
| - if selection > #items then selection = 1 end | |
| - update_data() | |
| + if #items > 1 then | |
| + selection[layer] = selection[layer] + 1 | |
| + if selection[layer] > #items then selection[layer] = 1 end | |
| + update_data() | |
| + end | |
| end | |
| local function key_left() | |
| - if not library_id then | |
| - return | |
| - elseif not title_id then | |
| - library_id = nil | |
| - selection = library_selection | |
| - elseif not season_id then | |
| - title_id = nil | |
| - selection = title_selection | |
| - else | |
| - season_id = nil | |
| - selection = season_selection | |
| - end | |
| + if layer == 1 then return end | |
| + layer = layer - 1 | |
| update_overlay() | |
| end | |
| @@ -237,7 +256,7 @@ end | |
| local function check_percent() | |
| local pos = mp.get_property_number("percent-pos") | |
| if pos then | |
| - if pos > 95 then | |
| + if pos > 95 and #video_id > 0 then | |
| send_request("POST", options.url.."/Users/"..user_id..… | |
| video_id = "" | |
| end | |
| @@ -252,4 +271,4 @@ os.execute("mkdir -p "..options.image_path) | |
| mp.add_periodic_timer(1, check_percent) | |
| mp.add_key_binding("Ctrl+j", "jf", toggle_overlay) | |
| mp.observe_property("osd-width", "number", width_change) | |
| -mp.register_event("file-loaded", unpause) | |
| +mp.register_event("end-file", unpause) |