From fbd1016bdbcc440b15b1a4a3e2b2f06ae6cd7e31 Mon Sep 17 00:00:00 2001 From: IAmParadox27 Date: Tue, 11 Feb 2025 20:45:33 +0000 Subject: [PATCH] Updated to include fix for playback and updated styling. --- .../Controllers/MediaBarController.cs | 11 + .../Inject/slideshowpure.css | 1343 ++++++++--------- .../Inject/slideshowpure.js | 936 ++++++------ .../Services/StartupService.cs | 8 + 4 files changed, 1114 insertions(+), 1184 deletions(-) diff --git a/src/Jellyfin.Plugin.MediaBar/Controllers/MediaBarController.cs b/src/Jellyfin.Plugin.MediaBar/Controllers/MediaBarController.cs index 38db0f9..e12eac0 100644 --- a/src/Jellyfin.Plugin.MediaBar/Controllers/MediaBarController.cs +++ b/src/Jellyfin.Plugin.MediaBar/Controllers/MediaBarController.cs @@ -53,5 +53,16 @@ namespace Jellyfin.Plugin.MediaBar.Controllers return Content(regex, "text/html"); } + + [HttpPost("Patch/MainJellyfinBundle")] + public ActionResult PatchMainBundle([FromBody] PatchRequestPayload payload) + { + string replacementText = + "window.PlaybackManager=this.playbackManager;console.log(\"PlaybackManager is now globally available:\",window.PlaybackManager);"; + + string regex = Regex.Replace(payload.Contents!, @"(this\.playbackManager=e,)", $"$1{replacementText}"); + + return Content(regex, "text/html"); + } } } \ No newline at end of file diff --git a/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.css b/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.css index 4afc598..e3b74dd 100644 --- a/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.css +++ b/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.css @@ -1,774 +1,687 @@ -@import url("https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700&display=swap"); @media only screen and (min-width: 767px) { - div[data-role="page"].page { - overflow-y: scroll; - scrollbar-width: none; - -ms-overflow-style: none; - } + div[data-role="page"].page { + overflow-y: scroll; + scrollbar-width: none; + -ms-overflow-style: none; + } - div[data-role="page"].page::-webkit-scrollbar { - display: none; - } + div[data-role="page"].page::-webkit-scrollbar { + display: none; + } - .backdrop, - .slide { - position: absolute; - } + .backdrop, + .slide { + position: absolute; + } - .featured-content, - .plot-container { - font-family: "Titillium Web", sans-serif; - left: 0; - } + .featured-content, + .plot-container { + font-family: "Titillium Web", sans-serif; + left: 0; + } - .plot, - body { - overflow: hidden; - } + .plot, + body { + overflow: hidden; + } - .arrow { - position: absolute; - top: 50%; - transform: translateY(-50%); - font-size: 24px; - cursor: pointer; - color: white; - z-index: 3; - display: flex; - justify-content: center; - align-items: center; - } + .arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + font-size: 24px; + cursor: pointer; + color: white; + z-index: 3; + display: flex; + justify-content: center; + align-items: center; + } - .arrow::before { - content: ""; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 40px; - height: 40px; - background-color: rgba(12, 110, 162, 0.7); - border-radius: 50%; - z-index: -1; - } + .arrow::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 40px; + height: 40px; + background-color: rgba(12, 110, 162, 0.7); + border-radius: 50%; + z-index: -1; + } - .left-arrow { - left: 10px; - } + .left-arrow { + left: 20px; + } - .right-arrow { - right: 10px; - } + .right-arrow { + right: 20px; + } - .dots-container { - position: absolute; - bottom: 5%; - left: 0; - right: 0; - z-index: 2; - display: flex; - justify-content: center; - align-items: center; - width: 100vw; - height: 22px; - background-color: #ffffff00; - gap: 2vw; - } + .dots-container { + position: absolute; + bottom: 5%; + left: 0; + right: 0; + z-index: 2; + display: flex; + justify-content: center; + align-items: center; + width: 100vw; + height: 22px; + background-color: #ffffff00; + gap: 2vw; + } - .dot { - width: 15vw; - height: 4px; - background-color: #cecece; - border-radius: 2px; - cursor: pointer; - transition: background-color 0.3s; - } + .dot { + width: 15vw; + height: 4px; + background-color: #cecece; + border-radius: 2px; + cursor: pointer; + transition: background-color 0.3s; + } - .dot.active { - background-color: rgba(12, 110, 162, 0.7) !important; - } + .dot.active { + background-color: rgba(12, 110, 162, 0.7) !important; + } - body { - margin: 0; - padding: 0; - } + body { + margin: 0; + padding: 0; + } - .fade-in { - opacity: 1 !important; - } + .fade-in { + opacity: 1 !important; + } - .fade-out { - opacity: 0 !important; - } + .fade-out { + opacity: 0 !important; + } - #slides-container { - position: relative; - width: 100vw; - height: 750px; - overflow: hidden; - margin: 0 auto; - border-radius: 7px; - top: -5em; - } + #slides-container { + position: relative; + width: 92vw; + height: 650px; + overflow: hidden; + margin: 0 auto; + border-radius: 15px; + } - .slide { - transition: opacity 0.5s ease-in-out; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: block; - } + .slide { + transition: opacity 0.5s ease-in-out; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: block; + } - .slide.active { - opacity: 1; - display: block; - } + .slide.active { + opacity: 1; + display: block; + } - .slide:focus { - outline: #fff solid 2px; - } + .slide:focus { + outline: #fff solid 2px; + } - .backdrop-container { - position: relative; - top: -40%; - right: -35%; - width: 70%; - height: 100%; - } + .backdrop-container { + position: relative; + top: -40%; + right: -35%; + width: 70%; + height: 100%; + } - .backdrop { - right: 0; - width: 100%; - height: 100%; - object-fit: cover; - object-position: center 20%; - border-radius: 5px; - z-index: 1; - mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - -webkit-mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - } + .backdrop { + right: 0; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center 20%; + border-radius: 5px; + z-index: 1; + mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + -webkit-mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + } - .backdrop-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.6); - border-radius: 5px; - z-index: 2; - mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - -webkit-mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - } + .backdrop-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 5px; + z-index: 2; + mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + -webkit-mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + } - .logo-container { - width: 45%; - height: 40%; - position: relative; - display: flex; - justify-content: left; - align-items: left; - } + .logo-container { + width: 45%; + height: 40%; + position: relative; + display: flex; + justify-content: left; + align-items: left; + } - .logo { - position: relative; - max-height: 100%; - max-width: 100%; - height: auto; - width: auto; - object-fit: contain; - z-index: 3; - top: 5em; - left: 8em; - } + .logo { + position: relative; + max-height: 100%; + max-width: 100%; + height: auto; + width: auto; + object-fit: contain; + z-index: 3; + top: 5em; + left: 8em; + } - .featured-content { - position: absolute; - top: 0; - width: 100%; - height: 50px; - background-color: transparent; - color: #d3d3d3; - font-size: 22px; - display: none; - align-items: center; - justify-content: flex-start; - z-index: 2; - } + .featured-content { + position: absolute; + top: 0; + width: 100%; + height: 50px; + background-color: transparent; + color: #d3d3d3; + font-size: 22px; + display: none; + align-items: center; + justify-content: flex-start; + z-index: 2; + } - .plot-container { - position: absolute; - bottom: 0; - color: #fff; - width: 50%; - height: 60%; - font-size: 15px; - padding: 10px 0 25px 15px; - border-radius: 5px; - z-index: 3; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - text-align: justify; - } + .plot-container { + position: absolute; + bottom: 0; + color: #fff; + width: 90%; + height: 60%; + font-size: 15px; + padding: 10px 0 25px 15px; + border-radius: 5px; + z-index: 3; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + text-align: justify; + } - .plot { - display: -webkit-box; - line-clamp: 4; - -webkit-line-clamp: 4; - -webkit-box-orient: vertical; - left: 7.9em; - position: absolute; - top: 190px; - } + .plot { + display: -webkit-box; + line-clamp: 4; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + left: 7.9em; + position: absolute; + top: 190px; + } - .gradient-overlay { - position: absolute; - top: 0; - left: 0; - width: 70%; - height: 100%; - background: linear-gradient(to right, #000 49%, rgba(0, 0, 0, 0) 70%); - z-index: 2; - mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 8%, - rgba(0, 0, 0, 0.5) 10%, - rgba(0, 0, 0, 1) 12% - ); - -webkit-mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 8%, - rgba(0, 0, 0, 0.5) 10%, - rgba(0, 0, 0, 1) 12% - ); - } - .genre { - position: absolute; - top: calc(50% + 75px); - left: 8.4em; - text-align: center; - color: #fff; - font-family: "Titillium Web", sans-serif; - font-size: 14px; - z-index: 4; - } - .info-container { - position: absolute; - top: calc(50% + 0px); - display: flex; - align-items: center; - justify-content: center; - left: 2em !important; - color: #fff; - z-index: 4; - } - .rating-value { - font-family: "Titillium Web", sans-serif; - display: flex; - align-items: center; - margin: 0 6em; - z-index: 5; - position: relative; - top: 1.5em; - } - .runTime { - font-family: "Titillium Web", sans-serif; - font-weight: 500; - display: flex; - align-items: center; - margin: 0 8px; - margin-right: -3px; - } - .rating-value img { - margin-right: 6px; - } - .tomato-rating { - display: flex; - align-items: center; - margin-left: 0; - } - .tomato-rating img { - margin-left: 5px; - } - .age-rating { - display: flex; - align-items: center; - margin-left: 5px; - margin-right: 5px; - border-radius: 5px; - background: rgb(255 255 255 / 0.8); - color: #000; - border: none; - font-weight: 600; - white-space: nowrap; - padding: 0 0.6em; - } - .date { - font-family: "Titillium Web", sans-serif; - font-weight: 500; - display: flex; - align-items: center; - margin-right: 5px; - margin-left: 2px; - } - .separator-icon { - margin-left: 5px; - margin-right: 5px; - font-size: 10px; - color: aquamarine; - } - .play-button { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - padding: 10px 20px; - color: #fff; - background-color: rgba(255, 255, 255, 0); - border: none; - border-radius: 50px; - cursor: pointer; - font-size: 14px; - font-weight: 300; - transition: background-color 0.3s; - z-index: 4; - top: calc(50% + 190px); - left: 6.8em; - } + .gradient-overlay { + position: absolute; + top: 0; + left: 0; + width: 70%; + height: 100%; + background: linear-gradient(to right, #000 49%, rgba(0, 0, 0, 0) 70%); + z-index: 2; + mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 8%, + rgba(0, 0, 0, 0.5) 10%, + rgba(0, 0, 0, 1) 12% + ); + -webkit-mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 8%, + rgba(0, 0, 0, 0.5) 10%, + rgba(0, 0, 0, 1) 12% + ); + } + .genre { + position: absolute; + top: calc(50% + 75px); + left: 8.4em; + text-align: center; + color: #fff; + font-family: "Titillium Web", sans-serif; + font-size: 14px; + z-index: 4; + } + .info-container { + position: absolute; + top: calc(50% + 0px); + display: flex; + align-items: center; + justify-content: center; + left: 2em !important; + color: #fff; + z-index: 4; + } + .rating-value { + font-family: "Titillium Web", sans-serif; + display: flex; + align-items: center; + margin: 0 6em; + z-index: 5; + position: relative; + top: 1.5em; + } + .runTime { + font-family: "Titillium Web", sans-serif; + font-weight: 500; + display: flex; + align-items: center; + margin: 0 8px; + margin-right: -3px; + } + .rating-value img { + margin-right: 6px; + } + .tomato-rating { + display: flex; + align-items: center; + margin-left: 0; + } + .tomato-rating img { + margin-left: 5px; + } + .age-rating { + display: flex; + align-items: center; + margin-left: 5px; + margin-right: 5px; + border-radius: 5px; + background: rgb(255 255 255 / 0.8); + color: #000; + border: none; + font-weight: 600; + white-space: nowrap; + padding: 0 0.6em; + } + .date { + font-family: "Titillium Web", sans-serif; + font-weight: 500; + display: flex; + align-items: center; + margin-right: 5px; + margin-left: 2px; + } + .separator-icon { + margin-left: 5px; + margin-right: 5px; + font-size: 10px; + color: aquamarine; + } + .button-container { + position: absolute; + top: calc(50% + 190px); /* Same as the previous button positions */ + left: 7.5em; + display: flex; + gap: 2em; /* Space between buttons */ + z-index: 5; /* Ensures it's above other elements */ + } + .play-button, + .detail-button { + display: inline-flex; /* Ensures icon and text are on the same line */ + align-items: center; + justify-content: center; + padding: 8px 16px; /* Slightly reduced padding for thinner buttons */ + border: solid 2px; + border-radius: 25px; /* Sleeker look */ + font-size: 14px; + white-space: nowrap; /* Prevents text wrapping */ + cursor: pointer; + transition: all 0.3s ease; + font-weight: bold; + gap: 6px; /* Adds space between icon and text */ + -webkit-tap-highlight-color: transparent; + } - .play-icon { - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - width: 30px; - height: 30px; - background-color: rgba( - 12, - 110, - 162, - 0.7 - ); - border: 3px solid rgba(12, 110, 162, 1); - border-radius: 50%; - } + .play-button { + background-color: #ffffff; + color: #000; + } - .play-text { - font-family: "Titillium Web", sans-serif; - margin-left: 10px; - font-size: 14px; - color: #fff; - } - .detail-button { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - padding: 10px 20px; - color: #fff; - background-color: rgba(255, 255, 255, 0); - border: none; - border-radius: 50px; - cursor: pointer; - font-size: 14px; - font-weight: 300; - transition: background-color 0.3s; - z-index: 4; - top: calc(50% + 190px); - left: 15.8em; - } + .detail-button { + background-color: rgba(255, 255, 255, 0); + color: #fff; + } - .detail-icon { - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - width: 30px; - height: 30px; - background-color: rgba( - 12, - 110, - 162, - 0.7 - ); - border: 3px solid rgba(12, 110, 162, 1); - border-radius: 50%; - } + .play-button:hover, + .detail-button:hover { + opacity: 0.8; + } - .detail-text { - font-family: "Titillium Web", sans-serif; - margin-left: 10px; - font-size: 14px; - color: #fff; - } + .play-icon, + .detail-icon { + font-size: 18px; /* Ensures icons are aligned with text */ + display: flex; + align-items: center; + } } - @media only screen and (max-width: 767px) { - .backdrop, - .slide { - position: absolute; - } + .backdrop, + .slide { + position: absolute; + } - .arrow { - position: absolute; - bottom: 25px; - font-size: 24px; - cursor: pointer; - color: rgba(255, 255, 255, 0.6); - z-index: 4; - -webkit-tap-highlight-color: transparent; - } + .arrow { + position: absolute; + bottom: 25px; + font-size: 24px; + cursor: pointer; + color: rgba(255, 255, 255, 0.6); + z-index: 4; + -webkit-tap-highlight-color: transparent; + } - .left-arrow { - left: 10px; - } + .left-arrow { + left: 10px; + } - .right-arrow { - right: 10px; - } + .right-arrow { + right: 10px; + } - .featured-content, - .plot-container { - font-family: "Titillium Web", sans-serif; - left: 0; - } + .featured-content, + .plot-container { + font-family: "Titillium Web", sans-serif; + left: 0; + } - .plot, - body { - overflow: hidden; - } + .plot, + body { + overflow: hidden; + } - body { - margin: 0; - padding: 0; - } + body { + margin: 0; + padding: 0; + } - .fade-in { - opacity: 1 !important; - } + .fade-in { + opacity: 1 !important; + } - .fade-out { - opacity: 0 !important; - } + .fade-out { + opacity: 0 !important; + } - .dots-container { - position: absolute; - bottom: 11%; - left: 50%; - transform: translateX(-50%) scale(0.6); - z-index: 2; - display: flex; - gap: 5px; - background-color: #ffffff00; - border-radius: 50px; - justify-content: center; - align-items: center; - width: 140px; - height: 22px; - transition: opacity 0.5s ease-in-out; - } + .dots-container { + position: absolute; + bottom: 11%; + left: 50%; + transform: translateX(-50%) scale(0.6); + z-index: 2; + display: flex; + gap: 5px; + background-color: #ffffff00; + border-radius: 50px; + justify-content: center; + align-items: center; + width: 140px; + height: 22px; + transition: opacity 0.5s ease-in-out; + } - .dot { - display: inline-block; - width: 9px; - height: 9px; - margin: 0 5px; - background-color: #cecece; - border-radius: 50%; - transition: background-color 0.3s; - } + .dot { + display: inline-block; + width: 9px; + height: 9px; + margin: 0 5px; + background-color: #cecece; + border-radius: 50%; + transition: background-color 0.3s; + } - .dot.active { - width: 32px; - height: 12px; - background-color: rgba(12, 110, 162, 0.7); - border-radius: 10px; - } + .dot.active { + width: 32px; + height: 12px; + background-color: rgba(12, 110, 162, 0.7); + border-radius: 10px; + } - #slides-container { - position: relative; - width: 92vw; - height: 650px; - overflow: hidden; - margin: 0 auto; - border-radius: 10px; - } + #slides-container { + position: relative; + width: 92vw; + height: 650px; + overflow: hidden; + margin: 0 auto; + border-radius: 10px; + } - .slide { - transition: opacity 0.5s ease-in-out; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - display: block; - } + .slide { + transition: opacity 0.5s ease-in-out; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: block; + } - .slide.active { - opacity: 1; - display: block; - } + .slide.active { + opacity: 1; + display: block; + } - .slide:focus { - outline: #fff solid 2px; - } - .logo { - position: absolute; - bottom: 28%; - left: 50%; - transform: translateX(-50%); - max-height: 60%; - max-width: 80%; - width: auto; - z-index: 3; - } + .slide:focus { + outline: #fff solid 2px; + } + .logo { + position: absolute; + bottom: 28%; + left: 50%; + transform: translateX(-50%); + max-height: 60%; + max-width: 80%; + width: auto; + z-index: 3; + } - .backdrop { - position: absolute; - top: 45px; - left: 0; - width: 100%; - height: calc(100% - 50px); - object-fit: cover; - object-position: center 20%; - z-index: 1; - border-radius: 13px; - mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - -webkit-mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - } + .backdrop { + position: absolute; + top: 45px; + left: 0; + width: 100%; + height: calc(100% - 50px); + object-fit: cover; + object-position: center 20%; + z-index: 1; + border-radius: 13px; + mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + -webkit-mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + } - .gradient-overlay { - position: absolute; - top: 45px; - left: 0; - width: 100%; - height: calc(100% - 50px); - background: rgba(0, 0, 0, 0.55); - z-index: 2; - border-radius: 13px; - pointer-events: none; - mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - -webkit-mask-image: linear-gradient( - to top, - rgba(0, 0, 0, 0) 10%, - rgba(0, 0, 0, 0.5) 15%, - rgba(0, 0, 0, 1) 30% - ); - } + .gradient-overlay { + position: absolute; + top: 45px; + left: 0; + width: 100%; + height: calc(100% - 50px); + background: rgba(0, 0, 0, 0.55); + z-index: 2; + border-radius: 13px; + pointer-events: none; + mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + -webkit-mask-image: linear-gradient( + to top, + rgba(0, 0, 0, 0) 10%, + rgba(0, 0, 0, 0.5) 15%, + rgba(0, 0, 0, 1) 30% + ); + } - .genre { - position: absolute; - bottom: 16%; - left: 50%; - transform: translateX(-50%); - text-align: center; - color: #fff; - width: 90%; - max-width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-family: "Titillium Web", sans-serif; - font-size: 1.1em; - font-weight: 500; - z-index: 4; - box-sizing: border-box; - } - .info-container { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - bottom: 20%; - left: 50%; - transform: translateX(-50%); - font-size: 1.1em; - color: #fff; - z-index: 5; - width: 100%; - } - .rating-value { - font-family: "Titillium Web", sans-serif; - font-weight: 500; - display: flex; - align-items: center; - } - .date { - font-family: "Titillium Web", sans-serif; - font-weight: 500; - display: flex; - align-items: center; - margin-left: 2px; - } - .tomato-rating { - display: flex; - align-items: center; - } - .tomato-logo { - margin-right: 3px; - } - .critic-logo { - margin-left: 3px; - } - .imdb-logo { - margin-right: 3px; - } - .age-rating { - display: flex; - align-items: center; - border-radius: 5px; - background: rgb(255 255 255 / 0.8); - color: #000; - border: none; - font-weight: 600; - white-space: nowrap; - padding: 0 0.4em; - } - .separator-icon { - margin-left: 5px; - margin-right: 5px; - font-size: 1px; - color: aquamarine; - } - .featured-content, - .plot { - display: none; - } + .genre { + position: absolute; + bottom: 16%; + left: 50%; + transform: translateX(-50%); + text-align: center; + color: #fff; + width: 90%; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-family: "Titillium Web", sans-serif; + font-size: 1.1em; + font-weight: 500; + z-index: 4; + box-sizing: border-box; + } + .info-container { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + bottom: 20%; + left: 50%; + transform: translateX(-50%); + font-size: 1.1em; + color: #fff; + z-index: 5; + width: 100%; + } + .rating-value { + font-family: "Titillium Web", sans-serif; + font-weight: 500; + display: flex; + align-items: center; + } + .date { + font-family: "Titillium Web", sans-serif; + font-weight: 500; + display: flex; + align-items: center; + margin-left: 2px; + } + .tomato-rating { + display: flex; + align-items: center; + } + .tomato-logo { + margin-right: 3px; + } + .critic-logo { + margin-left: 3px; + } + .imdb-logo { + margin-right: 3px; + } + .age-rating { + display: flex; + align-items: center; + border-radius: 5px; + background: rgb(255 255 255 / 0.8); + color: #000; + border: none; + font-weight: 600; + white-space: nowrap; + padding: 0 0.4em; + } + .separator-icon { + margin-left: 5px; + margin-right: 5px; + font-size: 1px; + color: aquamarine; + } + .featured-content, + .plot { + display: none; + } - .play-button { - position: absolute; - display: flex; - align-items: center; - justify-content: flex-start; - padding: 10px 20px; - color: #fff; - background-color: rgba(255, 255, 255, 0); - border: none; - border-radius: 50px; - cursor: pointer; - font-size: 14px; - font-weight: 300; - transition: background-color 0.3s; - z-index: 4; - bottom: 20px; - left: 50%; - transform: translateX(-110%) scale(1.2); - outline: none; - -webkit-tap-highlight-color: transparent; - } + .button-container { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 15px; /* Adjust spacing between buttons */ + z-index: 4; + } - .play-icon { - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - width: 20px; - height: 20px; - background-color: rgba( - 12, - 110, - 162, - 0.7 - ); - border: 1.5px solid rgba(12, 110, 162, 1); - border-radius: 50%; - outline: none; - -webkit-tap-highlight-color: transparent; - } + .play-button, + .detail-button { + display: inline-flex; /* Ensures icon and text are on the same line */ + align-items: center; + justify-content: center; + padding: 8px 16px; /* Slightly reduced padding for thinner buttons */ + border: none; + border-radius: 20px; /* Sleeker look */ + font-size: 14px; + white-space: nowrap; /* Prevents text wrapping */ + cursor: pointer; + transition: all 0.3s ease; + font-weight: bold; + gap: 6px; /* Adds space between icon and text */ + -webkit-tap-highlight-color: transparent; + } - .play-text { - display: block; - font-family: "Titillium Web", sans-serif; - margin-left: 5px; - font-size: 14px; - color: #fff; - } - .detail-button { - position: absolute; - display: flex; - align-items: center; - justify-content: flex-start; - padding: 10px 20px; - color: #fff; - background-color: rgba(255, 255, 255, 0); - border: none; - border-radius: 50px; - cursor: pointer; - font-size: 14px; - font-weight: 300; - transition: background-color 0.3s; - z-index: 4; - bottom: 20px; - left: 50%; - transform: translateX(10%) scale(1.2); - outline: none; - -webkit-tap-highlight-color: transparent; - } + .play-button { + background-color: #ffffff; + color: #000; + } - .detail-icon { - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - width: 20px; - height: 20px; - background-color: rgba( - 12, - 110, - 162, - 0.7 - ); - border: 1.5px solid rgba(12, 110, 162, 1); - border-radius: 50%; - outline: none; - -webkit-tap-highlight-color: transparent; - } + .detail-button { + background-color: rgba(255, 255, 255, 0.2); + color: #fff; + } - .detail-text { - display: block; - font-family: "Titillium Web", sans-serif; - margin-left: 5px; - color: #fff; - } -} + .play-button:hover, + .detail-button:hover { + opacity: 0.8; + } + + .play-icon, + .detail-icon { + font-size: 18px; /* Ensures icons are aligned with text */ + display: flex; + align-items: center; + } +} \ No newline at end of file diff --git a/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.js b/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.js index 548c8de..27dc4e0 100644 --- a/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.js +++ b/src/Jellyfin.Plugin.MediaBar/Inject/slideshowpure.js @@ -1,487 +1,485 @@ -const slidesInit = () => { - const shuffleInterval = 8000; - let isTransitioning = false; - const listFileName = `${window.location.origin}/web/avatars/list.txt`; - const jsonCredentials = sessionStorage.getItem("json-credentials"); - let userId = null; - let token = null; - //const deviceId = localStorage.getItem("_deviceId2"); - //console.log(deviceId); - if (jsonCredentials) { - const credentials = JSON.parse(jsonCredentials); - userId = credentials.Servers[0].UserId; - token = credentials.Servers[0].AccessToken; - } - const shuffleArray = (array) => array.sort(() => Math.random() - 0.5); - const truncateText = (element, maxLength) => { - let text = element.innerText; - if (text.length > maxLength) - element.innerText = text.substring(0, maxLength) + "..."; - }; - const createSlideElement = (item, title) => { - const itemId = item.Id; - const plot = item.Overview || "No overview available"; - const rating = item.CommunityRating; - const tomato = item.CriticRating; - const runtime = item.RunTimeTicks; - const genre = item.Genres; - const youtube = item.RemoteTrailers; - const age = item.OfficialRating; - const date = item.PremiereDate; - const season = item.ChildCount; - function createSeparator() { - const separatorHtml = - ''; - const tempDiv = document.createElement("div"); - tempDiv.innerHTML = separatorHtml; - return tempDiv.firstChild; +const slidesInit = () => { + const shuffleInterval = 8000; + let isTransitioning = false; + const listFileName = `${window.location.origin}/web/avatars/list.txt`; + const jsonCredentials = sessionStorage.getItem("json-credentials"); + let userId = null; + let token = null; + //const deviceId = localStorage.getItem("_deviceId2"); + //console.log(deviceId); + if (jsonCredentials) { + const credentials = JSON.parse(jsonCredentials); + userId = credentials.Servers[0].UserId; + token = credentials.Servers[0].AccessToken; } - const slide = document.createElement("a"); - slide.className = "slide"; - slide.target = "_top"; - slide.rel = "noreferrer"; - slide.tabIndex = 0; - slide.style.display = "none"; - const backdrop = document.createElement("img"); - backdrop.className = "backdrop"; - backdrop.src = `${window.location.origin}/Items/${itemId}/Images/Backdrop/0`; - backdrop.alt = "Backdrop"; - const backdropOverlay = document.createElement("div"); - backdropOverlay.className = "backdrop-overlay"; - const backdropContainer = document.createElement("div"); - backdropContainer.className = "backdrop-container"; - backdropContainer.appendChild(backdrop); - backdropContainer.appendChild(backdropOverlay); - const logo = document.createElement("img"); - logo.className = "logo"; - logo.src = `${window.location.origin}/Items/${itemId}/Images/Logo`; - logo.alt = "Logo"; - const logoContainer = document.createElement("div"); - logoContainer.className = "logo-container"; - logoContainer.appendChild(logo); - const featuredContent = document.createElement("div"); - featuredContent.className = "featured-content"; - featuredContent.textContent = title; - const plotElement = document.createElement("div"); - plotElement.className = "plot"; - plotElement.textContent = plot; - truncateText(plotElement, 360); - const plotContainer = document.createElement("div"); - plotContainer.className = "plot-container"; - plotContainer.appendChild(plotElement); - const gradientOverlay = document.createElement("div"); - gradientOverlay.className = "gradient-overlay"; - const runTimeElement = document.createElement("div"); - runTimeElement.className = "runTime"; - if (season === undefined) { - const milliseconds = runtime / 10000; - const currentTime = new Date(); - const endTime = new Date(currentTime.getTime() + milliseconds); - const options = { hour: "2-digit", minute: "2-digit", hour12: !0 }; - const formattedEndTime = endTime.toLocaleTimeString([], options); - runTimeElement.textContent = `Ends at ${formattedEndTime}`; - } else { - runTimeElement.textContent = `${season} Season${season > 1 ? "s" : ""}`; - } - const ratingTest = document.createElement("div"); - ratingTest.className = "rating-value"; - const imdbLogo = document.createElement("img"); - imdbLogo.className = "imdb-logo"; - imdbLogo.src = - "https://upload.wikimedia.org/wikipedia/commons/6/69/IMDB_Logo_2016.svg"; - imdbLogo.alt = "IMDb Logo"; - imdbLogo.style.width = "30px"; - imdbLogo.style.height = "30px"; - ratingTest.appendChild(imdbLogo); - if (typeof rating === "number") { - const formattedRating = rating.toFixed(1); - ratingTest.innerHTML += `${formattedRating} ⭐`; - } else { - console.error("Rating is undefined or not a number:", rating); - ratingTest.innerHTML += `N/A ⭐`; - } - ratingTest.appendChild(createSeparator()); - const tomatoRatingDiv = document.createElement("div"); - tomatoRatingDiv.className = "tomato-rating"; - const criticRating = item.CriticRating; - const tomatoLogo = document.createElement("img"); - tomatoLogo.className = "tomato-logo"; - const criticLogo = document.createElement("img"); - criticLogo.className = "critic-logo"; - tomatoLogo.src = - "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Rotten_Tomatoes_positive_audience.svg/1920px-Rotten_Tomatoes_positive_audience.svg.png"; - let valueElement; - if (typeof criticRating === "number") { - valueElement = document.createTextNode(`${criticRating}% `); - } else { - valueElement = document.createTextNode(`N/A `); - } - if (criticRating === undefined || criticRating <= 59) { - criticLogo.src = - "https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Rotten_Tomatoes_rotten.svg/1024px-Rotten_Tomatoes_rotten.svg.png"; - criticLogo.alt = "Rotten Tomato"; - } else { - criticLogo.src = "https://i.imgur.com/iMfwDk7.png"; - criticLogo.alt = "Fresh Tomato"; - } - tomatoLogo.style.width = "15px"; - tomatoLogo.style.height = "17px"; - criticLogo.style.width = "15px"; - criticLogo.style.height = "15px"; - tomatoRatingDiv.appendChild(tomatoLogo); - tomatoRatingDiv.appendChild(valueElement); - tomatoRatingDiv.appendChild(criticLogo); - tomatoRatingDiv.appendChild(createSeparator()); - const ageRatingDiv = document.createElement("div"); - ageRatingDiv.className = "age-rating"; - if (item.OfficialRating) { - ageRatingDiv.innerHTML = `${item.OfficialRating}`; - } else { - ageRatingDiv.innerHTML = `N/A`; - } - const calender = "📅"; - const premiereDate = document.createElement("div"); - premiereDate.className = "date"; - const year = date ? new Date(date).getFullYear() : "N/A"; - premiereDate.textContent = isNaN(year) ? "N/A" : year; - ratingTest.appendChild(tomatoRatingDiv); - ratingTest.appendChild(document.createTextNode(calender)); - ratingTest.appendChild(premiereDate); - ratingTest.appendChild(createSeparator()); - ratingTest.appendChild(ageRatingDiv); - ratingTest.appendChild(createSeparator()); - ratingTest.appendChild(runTimeElement); - const genresArray = item.Genres; - function parseGenres(genresArray) { - if (genresArray && genresArray.length > 0) { - return genresArray.slice(0, 3).join(" 🔹 "); - } else { - return "No Genre Available"; - } - } - const genreElement = document.createElement("div"); - genreElement.className = "genre"; - genreElement.innerHTML = parseGenres(genresArray); - const infoContainer = document.createElement("div"); - infoContainer.className = "info-container"; - infoContainer.appendChild(ratingTest); - // Create a container for the buttons -const buttonContainer = document.createElement("div"); -buttonContainer.className = "button-container"; // Add a class for styling + const shuffleArray = (array) => array.sort(() => Math.random() - 0.5); + const truncateText = (element, maxLength) => { + let text = element.innerText; + if (text.length > maxLength) + element.innerText = text.substring(0, maxLength) + "..."; + }; + const createSlideElement = (item, title) => { + const itemId = item.Id; + const plot = item.Overview || "No overview available"; + const rating = item.CommunityRating; + const tomato = item.CriticRating; + const runtime = item.RunTimeTicks; + const genre = item.Genres; + const youtube = item.RemoteTrailers; + const age = item.OfficialRating; + const date = item.PremiereDate; + const season = item.ChildCount; + function createSeparator() { + const separatorHtml = + ''; + const tempDiv = document.createElement("div"); + tempDiv.innerHTML = separatorHtml; + return tempDiv.firstChild; + } + const slide = document.createElement("a"); + slide.className = "slide"; + slide.target = "_top"; + slide.rel = "noreferrer"; + slide.tabIndex = 0; + slide.style.display = "none"; + const backdrop = document.createElement("img"); + backdrop.className = "backdrop"; + backdrop.src = `${window.location.origin}/Items/${itemId}/Images/Backdrop/0`; + backdrop.alt = "Backdrop"; + const backdropOverlay = document.createElement("div"); + backdropOverlay.className = "backdrop-overlay"; + const backdropContainer = document.createElement("div"); + backdropContainer.className = "backdrop-container"; + backdropContainer.appendChild(backdrop); + backdropContainer.appendChild(backdropOverlay); + const logo = document.createElement("img"); + logo.className = "logo"; + logo.src = `${window.location.origin}/Items/${itemId}/Images/Logo`; + logo.alt = "Logo"; + const logoContainer = document.createElement("div"); + logoContainer.className = "logo-container"; + logoContainer.appendChild(logo); + const featuredContent = document.createElement("div"); + featuredContent.className = "featured-content"; + featuredContent.textContent = title; + const plotElement = document.createElement("div"); + plotElement.className = "plot"; + plotElement.textContent = plot; + truncateText(plotElement, 360); + const plotContainer = document.createElement("div"); + plotContainer.className = "plot-container"; + plotContainer.appendChild(plotElement); + const gradientOverlay = document.createElement("div"); + gradientOverlay.className = "gradient-overlay"; + const runTimeElement = document.createElement("div"); + runTimeElement.className = "runTime"; + if (season === undefined) { + const milliseconds = runtime / 10000; + const currentTime = new Date(); + const endTime = new Date(currentTime.getTime() + milliseconds); + const options = { hour: "2-digit", minute: "2-digit", hour12: !0 }; + const formattedEndTime = endTime.toLocaleTimeString([], options); + runTimeElement.textContent = `Ends at ${formattedEndTime}`; + } else { + runTimeElement.textContent = `${season} Season${season > 1 ? "s" : ""}`; + } + const ratingTest = document.createElement("div"); + ratingTest.className = "rating-value"; + const imdbLogo = document.createElement("img"); + imdbLogo.className = "imdb-logo"; + imdbLogo.src = + "https://upload.wikimedia.org/wikipedia/commons/6/69/IMDB_Logo_2016.svg"; + imdbLogo.alt = "IMDb Logo"; + imdbLogo.style.width = "30px"; + imdbLogo.style.height = "30px"; + ratingTest.appendChild(imdbLogo); + if (typeof rating === "number") { + const formattedRating = rating.toFixed(1); + ratingTest.innerHTML += `${formattedRating} ⭐`; + } else { + console.error("Rating is undefined or not a number:", rating); + ratingTest.innerHTML += `N/A ⭐`; + } + ratingTest.appendChild(createSeparator()); + const tomatoRatingDiv = document.createElement("div"); + tomatoRatingDiv.className = "tomato-rating"; + const criticRating = item.CriticRating; + const tomatoLogo = document.createElement("img"); + tomatoLogo.className = "tomato-logo"; + const criticLogo = document.createElement("img"); + criticLogo.className = "critic-logo"; + tomatoLogo.src = + "https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Rotten_Tomatoes_positive_audience.svg/1920px-Rotten_Tomatoes_positive_audience.svg.png"; + let valueElement; + if (typeof criticRating === "number") { + valueElement = document.createTextNode(`${criticRating}% `); + } else { + valueElement = document.createTextNode(`N/A `); + } + if (criticRating === undefined || criticRating <= 59) { + criticLogo.src = + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/52/Rotten_Tomatoes_rotten.svg/1024px-Rotten_Tomatoes_rotten.svg.png"; + criticLogo.alt = "Rotten Tomato"; + } else { + criticLogo.src = "https://i.imgur.com/iMfwDk7.png"; + criticLogo.alt = "Fresh Tomato"; + } + tomatoLogo.style.width = "15px"; + tomatoLogo.style.height = "17px"; + criticLogo.style.width = "15px"; + criticLogo.style.height = "15px"; + tomatoRatingDiv.appendChild(tomatoLogo); + tomatoRatingDiv.appendChild(valueElement); + tomatoRatingDiv.appendChild(criticLogo); + tomatoRatingDiv.appendChild(createSeparator()); + const ageRatingDiv = document.createElement("div"); + ageRatingDiv.className = "age-rating"; + if (item.OfficialRating) { + ageRatingDiv.innerHTML = `${item.OfficialRating}`; + } else { + ageRatingDiv.innerHTML = `N/A`; + } + const calender = "📅"; + const premiereDate = document.createElement("div"); + premiereDate.className = "date"; + const year = date ? new Date(date).getFullYear() : "N/A"; + premiereDate.textContent = isNaN(year) ? "N/A" : year; + ratingTest.appendChild(tomatoRatingDiv); + ratingTest.appendChild(document.createTextNode(calender)); + ratingTest.appendChild(premiereDate); + ratingTest.appendChild(createSeparator()); + ratingTest.appendChild(ageRatingDiv); + ratingTest.appendChild(createSeparator()); + ratingTest.appendChild(runTimeElement); + const genresArray = item.Genres; + function parseGenres(genresArray) { + if (genresArray && genresArray.length > 0) { + return genresArray.slice(0, 3).join(" 🔹 "); + } else { + return "No Genre Available"; + } + } + const genreElement = document.createElement("div"); + genreElement.className = "genre"; + genreElement.innerHTML = parseGenres(genresArray); + const infoContainer = document.createElement("div"); + infoContainer.className = "info-container"; + infoContainer.appendChild(ratingTest); + // Create a container for the buttons + const buttonContainer = document.createElement("div"); + buttonContainer.className = "button-container"; // Add a class for styling -// Create the Play button -const playButton = document.createElement("button"); -playButton.className = "play-button"; -playButton.innerHTML = ` + // Create the Play button + const playButton = document.createElement("button"); + playButton.className = "play-button"; + playButton.innerHTML = ` - Play Now + Play `; -playButton.onclick = async () => { - if (!window.ApiClient) { - console.error("Jellyfin API client is not available."); - return; - } - const apiClient = window.ApiClient; - const userId = apiClient.getCurrentUserId(); - if (!userId) { - console.error("User not found."); - return; - } - try { - const sessions = await apiClient.getJSON(apiClient.serverAddress() + "/Sessions"); - if (!sessions.length) { - console.error("No active Jellyfin sessions found."); - return; - } + playButton.onclick = async () => { + if (!window.PlaybackManager) { + console.error("PlaybackManager is not available."); + return; + } - // Find the session matching the current user - const userSession = sessions.find(session => session.UserId === userId); - if (!userSession) { - console.error("No active session found for the current user."); - return; - } + if (!window.ApiClient) { + console.error("Jellyfin API client is not available."); + return; + } - const sessionId = userSession.Id; - const playbackUrl = `${apiClient.serverAddress()}/Sessions/${sessionId}/Playing?StartPositionTicks=0&PlayCommand=PlayNow&StartIndex=0&ItemIds=${itemId}&api_key=${token}`; + const apiClient = window.ApiClient; + const userId = apiClient.getCurrentUserId(); - const response = await fetch(playbackUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `DeviceId="unique-device-id", Token="${token}"`, - }, - }); + if (!userId) { + console.error("User not found."); + return; + } - if (response.status === 204) { - console.log("Playback started successfully."); - } else { - const errorData = await response.json(); - console.error("Failed to start playback:", errorData); - } - } catch (error) { - console.error("Error fetching sessions:", error); - } -}; + try { + // Fetch the media item details + const item = await apiClient.getItem(userId, itemId); + if (!item) { + console.error("Media item not found."); + return; + } -// Create the Details button -const detailButton = document.createElement("button"); -detailButton.className = "detail-button"; -detailButton.innerHTML = ` + // Initiate playback using PlaybackManager + window.PlaybackManager.play({ + items: [item], + startPositionTicks: 0, // Start from beginning + isMuted: false, + isPaused: false, + }) + .then(() => { + console.log("Playback started successfully."); + }) + .catch((error) => { + console.error("Failed to start playback:", error); + }); + } catch (error) { + console.error("Error starting playback:", error); + } + }; + + // Create the Details button + const detailButton = document.createElement("button"); + detailButton.className = "detail-button"; + detailButton.innerHTML = ` - More Info + Info `; -detailButton.onclick = () => { - window.top.location.href = `/#!/details?id=${itemId}`; -}; + detailButton.onclick = () => { + window.top.location.href = `/#!/details?id=${itemId}`; + }; -// Append buttons to the button container -buttonContainer.appendChild(playButton); -buttonContainer.appendChild(detailButton); + // Append buttons to the button container + buttonContainer.appendChild(playButton); + buttonContainer.appendChild(detailButton); -// Append all elements to the slide -slide.append( - logoContainer, - backdropContainer, - gradientOverlay, - featuredContent, - plotContainer, - infoContainer, - genreElement, - buttonContainer // Add button container instead of individual buttons -); + // Append all elements to the slide + slide.append( + logoContainer, + backdropContainer, + gradientOverlay, + featuredContent, + plotContainer, + infoContainer, + genreElement, + buttonContainer + ); -return slide; - }; - const createSlideForItem = async (item, title) => { - const container = document.getElementById("slides-container"); - const itemId = item.Id; - const backdropUrl = `${window.location.origin}/Items/${itemId}/Images/Backdrop/0`; - const logoUrl = `${window.location.origin}/Items/${itemId}/Images/Logo`; - const [backdropExists, logoExists] = await Promise.all([ - fetch(backdropUrl, { method: "HEAD" }).then((res) => res.ok), - fetch(logoUrl, { method: "HEAD" }).then((res) => res.ok), - ]); - if (backdropExists && logoExists) { - const slideElement = createSlideElement(item, title); - container.appendChild(slideElement); - console.log(`Added slide for item ${itemId}`); - if (container.children.length === 1) { - showSlide(0); - } - } else { - console.warn(`Skipping item ${itemId}: Missing backdrop or logo.`); - } - }; - const fetchItemDetails = async (itemId) => { - const response = await fetch( - `${window.location.origin}/Users/${userId}/Items/${itemId}`, - { - headers: { - Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`, - }, - } - ); - const item = await response.json(); - console.log("Item Title:", item.Name); - return item; - }; - const fetchItemIdsFromList = async () => { - try { - const response = await fetch(listFileName); - if (!response.ok) { - throw new Error("Failed to fetch list.txt"); - } - const text = await response.text(); - return text - .split("\n") - .map((id) => id.trim()) - .filter((id) => id); - } catch (error) { - console.error("Error fetching list.txt:", error); - return []; - } - }; - const fetchItemsFromServer = async () => { - try { - const response = await fetch( - `${window.location.origin}/Users/${userId}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&isPlayed=False&Limit=150`, - { - headers: { - Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`, - }, - } - ); - const data = await response.json(); - const items = data.Items; - const movies = items.filter((item) => item.Type === "Movie"); - const tvShows = items.filter((item) => item.Type === "Series"); - const shuffledMovies = shuffleArray(movies); - const shuffledTvShows = shuffleArray(tvShows); - const selectedMovies = shuffledMovies.slice(0, 15); - const selectedTvShows = shuffledTvShows.slice(0, 15); - const allItems = []; - const maxLength = Math.max(selectedMovies.length, selectedTvShows.length); - for (let i = 0; i < maxLength; i++) { - if (i < selectedMovies.length) allItems.push(selectedMovies[i]); - if (i < selectedTvShows.length) allItems.push(selectedTvShows[i]); - } - return allItems; - } catch (error) { - console.error("Error fetching items:", error); - return []; - } - }; - const createSlidesForItems = async (items) => { - await Promise.all( - items.map((item) => - createSlideForItem(item, item.Type === "Movie" ? "Movie" : "TV Show") - ) - ); - }; - const createPaginationDots = () => { - const dotsContainer = document.createElement("div"); - dotsContainer.className = "dots-container"; - for (let i = 0; i < 5; i++) { - const dot = document.createElement("span"); - dot.className = "dot"; - dotsContainer.appendChild(dot); - } - const container = document.getElementById("slides-container"); - container.appendChild(dotsContainer); - }; - const showSlide = (index) => { - const slides = document.querySelectorAll(".slide"); - const dots = document.querySelectorAll(".dot"); - slides.forEach((slide, i) => { - if (i === index) { - slide.style.display = "block"; - slide.offsetHeight; - slide.style.opacity = "1"; - slide.classList.add("active"); - } else { - slide.style.opacity = "0"; - slide.classList.remove("active"); - setTimeout(() => { - slide.style.display = "none"; - }, 500); - } - }); - dots.forEach((dot, i) => { - dot.classList.toggle("active", i === index % 5); - }); - }; - const initializeSlideshow = () => { - const slides = document.querySelectorAll(".slide"); - createPaginationDots(); - const container = document.getElementById("slides-container"); - let currentSlideIndex = 0; - let focusedSlide = null; - let containerFocused = !1; - const updateCurrentSlide = (index) => { - if (isTransitioning) return; - isTransitioning = true; - currentSlideIndex = (index + slides.length) % slides.length; - showSlide(currentSlideIndex); - setTimeout(() => { - isTransitioning = false; - }, 500); + return slide; }; - const openActiveSlide = () => { - if (focusedSlide) { - window.location.href = focusedSlide.href; - } - }; - if (slides.length > 0) { - showSlide(currentSlideIndex); - container.style.display = "block"; - setTimeout(() => { - setInterval(() => { - updateCurrentSlide(currentSlideIndex + 1); - }, shuffleInterval); - }, 8000); - } - slides.forEach((slide) => { - slide.addEventListener( - "focus", - () => { - focusedSlide = slide; - container.classList.remove("disable-interaction"); - }, - !0 - ); - slide.addEventListener( - "blur", - () => { - if (focusedSlide === slide) { - focusedSlide = null; - } - }, - !0 - ); - }); - document.addEventListener("keydown", (event) => { - if (containerFocused) { - switch (event.keyCode) { - case 37: - updateCurrentSlide(currentSlideIndex - 1); - break; - case 39: - updateCurrentSlide(currentSlideIndex + 1); - break; - case 13: - case 32: - openActiveSlide(); - break; + const createSlideForItem = async (item, title) => { + const container = document.getElementById("slides-container"); + const itemId = item.Id; + const backdropUrl = `${window.location.origin}/Items/${itemId}/Images/Backdrop/0`; + const logoUrl = `${window.location.origin}/Items/${itemId}/Images/Logo`; + const [backdropExists, logoExists] = await Promise.all([ + fetch(backdropUrl, { method: "HEAD" }).then((res) => res.ok), + fetch(logoUrl, { method: "HEAD" }).then((res) => res.ok), + ]); + if (backdropExists && logoExists) { + const slideElement = createSlideElement(item, title); + container.appendChild(slideElement); + console.log(`Added slide for item ${itemId}`); + if (container.children.length === 1) { + showSlide(0); + } + } else { + console.warn(`Skipping item ${itemId}: Missing backdrop or logo.`); } - } - }); - const leftArrow = document.createElement("div"); - const rightArrow = document.createElement("div"); - leftArrow.classList.add("arrow", "left-arrow"); - rightArrow.classList.add("arrow", "right-arrow"); - leftArrow.innerHTML = ''; - rightArrow.innerHTML = ''; - container.appendChild(leftArrow); - container.appendChild(rightArrow); - leftArrow.addEventListener("click", () => - updateCurrentSlide(currentSlideIndex - 1) - ); - rightArrow.addEventListener("click", () => - updateCurrentSlide(currentSlideIndex + 1) - ); - container.addEventListener("focus", () => { - containerFocused = !0; - }); - container.addEventListener("blur", () => { - containerFocused = !1; - }); - }; - const initializeSlides = async () => { - const itemIds = await fetchItemIdsFromList(); - let items; - if (itemIds.length > 0) { - const itemPromises = itemIds.map((id) => fetchItemDetails(id)); - items = await Promise.all(itemPromises); - } else { - const allItems = await fetchItemsFromServer(); - const itemPromises = allItems.map((item) => fetchItemDetails(item.Id)); - items = await Promise.all(itemPromises); - } - items = shuffleArray(items); - await createSlidesForItems(items); - initializeSlideshow(); - }; - initializeSlides(); + }; + const fetchItemDetails = async (itemId) => { + const response = await fetch( + `${window.location.origin}/Users/${userId}/Items/${itemId}`, + { + headers: { + Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`, + }, + } + ); + const item = await response.json(); + console.log("Item Title:", item.Name); + return item; + }; + const fetchItemIdsFromList = async () => { + try { + const response = await fetch(listFileName); + if (!response.ok) { + throw new Error("Failed to fetch list.txt"); + } + const text = await response.text(); + return text + .split("\n") + .map((id) => id.trim()) + .filter((id) => id); + } catch (error) { + console.error("Error fetching list.txt:", error); + return []; + } + }; + const fetchItemsFromServer = async () => { + try { + const response = await fetch( + `${window.location.origin}/Users/${userId}/Items?IncludeItemTypes=Movie,Series&Recursive=true&hasOverview=true&imageTypes=Logo,Backdrop&isPlayed=False&Limit=150`, + { + headers: { + Authorization: `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"`, + }, + } + ); + const data = await response.json(); + const items = data.Items; + const movies = items.filter((item) => item.Type === "Movie"); + const tvShows = items.filter((item) => item.Type === "Series"); + const shuffledMovies = shuffleArray(movies); + const shuffledTvShows = shuffleArray(tvShows); + const selectedMovies = shuffledMovies.slice(0, 15); + const selectedTvShows = shuffledTvShows.slice(0, 15); + const allItems = []; + const maxLength = Math.max(selectedMovies.length, selectedTvShows.length); + for (let i = 0; i < maxLength; i++) { + if (i < selectedMovies.length) allItems.push(selectedMovies[i]); + if (i < selectedTvShows.length) allItems.push(selectedTvShows[i]); + } + return allItems; + } catch (error) { + console.error("Error fetching items:", error); + return []; + } + }; + const createSlidesForItems = async (items) => { + await Promise.all( + items.map((item) => + createSlideForItem(item, item.Type === "Movie" ? "Movie" : "TV Show") + ) + ); + }; + const createPaginationDots = () => { + const dotsContainer = document.createElement("div"); + dotsContainer.className = "dots-container"; + for (let i = 0; i < 5; i++) { + const dot = document.createElement("span"); + dot.className = "dot"; + dotsContainer.appendChild(dot); + } + const container = document.getElementById("slides-container"); + container.appendChild(dotsContainer); + }; + const showSlide = (index) => { + const slides = document.querySelectorAll(".slide"); + const dots = document.querySelectorAll(".dot"); + slides.forEach((slide, i) => { + if (i === index) { + slide.style.display = "block"; + slide.offsetHeight; + slide.style.opacity = "1"; + slide.classList.add("active"); + } else { + slide.style.opacity = "0"; + slide.classList.remove("active"); + setTimeout(() => { + slide.style.display = "none"; + }, 500); + } + }); + dots.forEach((dot, i) => { + dot.classList.toggle("active", i === index % 5); + }); + }; + const initializeSlideshow = () => { + const slides = document.querySelectorAll(".slide"); + createPaginationDots(); + const container = document.getElementById("slides-container"); + let currentSlideIndex = 0; + let focusedSlide = null; + let containerFocused = !1; + const updateCurrentSlide = (index) => { + if (isTransitioning) return; + isTransitioning = true; + currentSlideIndex = (index + slides.length) % slides.length; + showSlide(currentSlideIndex); + setTimeout(() => { + isTransitioning = false; + }, 500); + }; + const openActiveSlide = () => { + if (focusedSlide) { + window.location.href = focusedSlide.href; + } + }; + if (slides.length > 0) { + showSlide(currentSlideIndex); + container.style.display = "block"; + setTimeout(() => { + setInterval(() => { + updateCurrentSlide(currentSlideIndex + 1); + }, shuffleInterval); + }, 8000); + } + slides.forEach((slide) => { + slide.addEventListener( + "focus", + () => { + focusedSlide = slide; + container.classList.remove("disable-interaction"); + }, + !0 + ); + slide.addEventListener( + "blur", + () => { + if (focusedSlide === slide) { + focusedSlide = null; + } + }, + !0 + ); + }); + document.addEventListener("keydown", (event) => { + if (containerFocused) { + switch (event.keyCode) { + case 37: + updateCurrentSlide(currentSlideIndex - 1); + break; + case 39: + updateCurrentSlide(currentSlideIndex + 1); + break; + case 13: + case 32: + openActiveSlide(); + break; + } + } + }); + const leftArrow = document.createElement("div"); + const rightArrow = document.createElement("div"); + leftArrow.classList.add("arrow", "left-arrow"); + rightArrow.classList.add("arrow", "right-arrow"); + leftArrow.innerHTML = ''; + rightArrow.innerHTML = ''; + container.appendChild(leftArrow); + container.appendChild(rightArrow); + leftArrow.addEventListener("click", () => + updateCurrentSlide(currentSlideIndex - 1) + ); + rightArrow.addEventListener("click", () => + updateCurrentSlide(currentSlideIndex + 1) + ); + container.addEventListener("focus", () => { + containerFocused = !0; + }); + container.addEventListener("blur", () => { + containerFocused = !1; + }); + }; + const initializeSlides = async () => { + const itemIds = await fetchItemIdsFromList(); + let items; + if (itemIds.length > 0) { + const itemPromises = itemIds.map((id) => fetchItemDetails(id)); + items = await Promise.all(itemPromises); + } else { + const allItems = await fetchItemsFromServer(); + const itemPromises = allItems.map((item) => fetchItemDetails(item.Id)); + items = await Promise.all(itemPromises); + } + items = shuffleArray(items); + await createSlidesForItems(items); + initializeSlideshow(); + }; + initializeSlides(); }; document.addEventListener("DOMContentLoaded", () => { - document.body.addEventListener("click", (event) => { - const homeButton = event.target.matches( - ".headerHomeButton, .css-17c09up, .mainDrawer-scrollContainer > a:nth-child(2)" - ); - if (homeButton) { - event.preventDefault(); - window.location.href = "/web/index.html#/home.html"; - setTimeout(handleHomeNavigation, 300); - } - }); -}); + document.body.addEventListener("click", (event) => { + const homeButton = event.target.matches( + ".headerHomeButton, .css-17c09up, .mainDrawer-scrollContainer > a:nth-child(2)" + ); + if (homeButton) { + event.preventDefault(); + window.location.href = "/web/index.html#/home.html"; + setTimeout(handleHomeNavigation, 300); + } + }); +}); \ No newline at end of file diff --git a/src/Jellyfin.Plugin.MediaBar/Services/StartupService.cs b/src/Jellyfin.Plugin.MediaBar/Services/StartupService.cs index 4f45027..ced7f3e 100644 --- a/src/Jellyfin.Plugin.MediaBar/Services/StartupService.cs +++ b/src/Jellyfin.Plugin.MediaBar/Services/StartupService.cs @@ -48,6 +48,14 @@ namespace Jellyfin.Plugin.MediaBar.Services payloads.Add(payload); } + { + JObject payload = new JObject(); + payload.Add("id", "3d171ef1-a198-48ac-9a60-f6aa98e5fd6d"); + payload.Add("fileNamePattern", "main.jellyfin.bundle.js"); + payload.Add("tranformationEndpoint", "/MediaBar/Patch/MainJellyfinBundle"); + + payloads.Add(payload); + } string? publishedServerUrl = m_serverApplicationHost.GetType() .GetProperty("PublishedServerUrl", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(m_serverApplicationHost) as string;