Compare commits
48 Commits
273c78dea2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b60ff031e | ||
|
|
c9cedf909f | ||
|
|
b46dae9ff0 | ||
|
|
078ec24814 | ||
|
|
c18282396c | ||
|
|
01469c1b06 | ||
|
|
8f92dcaca3 | ||
|
|
4f42929965 | ||
|
|
cc67c3abc0 | ||
|
|
1b05d4f863 | ||
|
|
1748a8ab3d | ||
|
|
2c9e3defd8 | ||
|
|
4323cdb367 | ||
|
|
018f78a6df | ||
|
|
a3449ec834 | ||
|
|
fc3d695af6 | ||
|
|
6828e93bda | ||
| d26c7667c9 | |||
| 062c22bfa8 | |||
| 632a27983e | |||
| de1ef45c41 | |||
| 353e7714bf | |||
| 1b828c511a | |||
| 252ca17a34 | |||
| 5e8eede1cd | |||
| 55a19ce7e5 | |||
| e6384c096b | |||
| d80c8c01ee | |||
| cdceb555f0 | |||
| 548aafb019 | |||
| 50756f3c2f | |||
| 3bdd185a53 | |||
| 5362f15b80 | |||
| 361698e84b | |||
| d6214d0441 | |||
| 232f55923f | |||
| 3105ed3088 | |||
| 7b2f5acb21 | |||
| 8018010aae | |||
| d9e638ec9e | |||
| ef5f893637 | |||
| a4a8b4e7f2 | |||
| bcac8a402a | |||
| d9b216ce49 | |||
| 161fad5fc4 | |||
| 753b821480 | |||
| 83c9f601d2 | |||
| f48b67bc29 |
171
README.md
@@ -1,39 +1,118 @@
|
|||||||
# SpotlightTrailer - Featured Content Bar
|
# SpotlightTrailer - Featured Content Bar
|
||||||
Thanks to [SethBacon](https://forum.jellyfin.org/u-sethbacon) & [BobHasNoSoul](https://github.com/BobHasNoSoul) & [MakD](https://github.com/MakD) & [tedhinklater](https://github.com/tedhinklater) for their talents and work
|
Thanks to [SethBacon](https://forum.jellyfin.org/u-sethbacon) & [BobHasNoSoul](https://github.com/BobHasNoSoul) & [MakD](https://github.com/MakD) & [tedhinklater](https://github.com/tedhinklater) for their talents and work
|
||||||
|
|
||||||
# Main Differences in this fork
|
## Main Differences in this fork
|
||||||
- activate random selection of movie from txt list
|
- automatically skip outros/endcards and intros on trailers using SponsorBlock API
|
||||||
- show the featured bar only at the main page, hide it in favorites/requests tab
|
- show the featured bar only at the main page, hide it in favorites/requests tab
|
||||||
- back button added
|
- automatically switch between diffrent seasonal lists
|
||||||
- set txt list name
|
|
||||||
- small ui style fixes
|
- small ui style fixes
|
||||||
|
- html, css and javascript in seperate files
|
||||||
|
- display title for featured content (optional)
|
||||||
|
- **no hard coded api key needed**
|
||||||
|
|
||||||
# Currently working on
|
Testet on Jellyfin 10.10.7
|
||||||
- show a title above the spotlight banner, e.g. 'Popular at Jellyfin'
|
|
||||||
- if local trailer available, play it instead of youtube trailer
|
## Table of Contents
|
||||||
|
- [SpotlightTrailer - Featured Content Bar](#spotlighttrailer---featured-content-bar)
|
||||||
|
- [Main Differences in this fork](#main-differences-in-this-fork)
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
|
- [Configuration Parameters](#configuration-parameters)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Uninstall](#uninstall)
|
||||||
|
- [How to feature specific content in the bar](#how-to-feature-specific-content-in-the-bar)
|
||||||
|
- [Desktop View](#desktop-view)
|
||||||
|
- [Mobile View (Landscape / Portrait)](#mobile-view-landscape--portrait)
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
Testet on Jellyfin 10.10.0
|

|
||||||
|
|
||||||
# Impressions
|
## Configuration Parameters
|
||||||

|
|
||||||
|
The following configuration parameters are used to customize the behavior and appearance of the slideshow. You can adjust them at the beginning of `script.js`:
|
||||||
|
|
||||||
|
- **`title`**:
|
||||||
|
The title of the slideshow. Set this to true to show the title from first line of `list.txt` (see [list.txt](/list.txt)).
|
||||||
|

|
||||||
|
|
||||||
|
- **`useSeasonalLists`**:
|
||||||
|
Use seasonal lists, see [seasonal lists doc](/SEASONAL_LISTS.md).
|
||||||
|
|
||||||
|
- **`listFileName`**:
|
||||||
|
The name of the file containing the list of movie or series IDs. Ensure this file exists in the correct location.
|
||||||
|
|
||||||
|
- **`moviesSeriesBoth`**:
|
||||||
|
Specifies the type of content to display:
|
||||||
|
- `1` for movies only,
|
||||||
|
- `2` for series only,
|
||||||
|
- `3` for both.
|
||||||
|
|
||||||
|
Default is `3`
|
||||||
|
|
||||||
|
- **`shuffleInterval`**:
|
||||||
|
Time interval (in milliseconds) between slides, unless a trailer is playing. Adjust for desired slide transition speed.
|
||||||
|
|
||||||
|
- **`useTrailers`**:
|
||||||
|
Enable (`true`) or disable (`false`) the display of trailers in the slideshow.
|
||||||
|
|
||||||
|
- **`setRandomMovie`**:
|
||||||
|
Enable (`true`) or disable (`false`) random selection of movies or series from the list. Default is `true`
|
||||||
|
|
||||||
|
- **`showOnOtherPages`**:
|
||||||
|
Set to `true` to show the slideshow on additional pages, such as "Favorites" or "Requests." Default is `false`.
|
||||||
|
|
||||||
|
- **`disableTrailerControls`**:
|
||||||
|
Set to `true` to hide trailer controls; `false` enables user control over the trailer. Default is `true`
|
||||||
|
|
||||||
|
- **`setMutedHover`**:
|
||||||
|
Set to `false` to disable unmuting the video on hover
|
||||||
|
Default mute setting for trailers on hover over entire slideshow element:
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`umuteOnHover`**:
|
||||||
|
Unmute video when hovered over (`true`) or keep muted (`false`).
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`unmutedVolume`**:
|
||||||
|
Volume level (0–100) when unmuted.
|
||||||
|
Default is `20`
|
||||||
|
|
||||||
|
- **`useSponsorBlock`**:
|
||||||
|
Enable (`true`) or disable (`false`) the use of SponsorBlock data for skipping segments (outros, intros) in trailers.
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`skipIntro`**:
|
||||||
|
Enable (`true`) or disable (`false`) to skip the intro segment of the trailer.
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`plotMaxLength`**:
|
||||||
|
Maximum number of characters for plot descriptions. Adjust as needed.
|
||||||
|
|
||||||
|
- **`trailerMaxLength`**:
|
||||||
|
Maximum duration (in milliseconds) of trailers. Set to `0` to disable trailer length limitation. Could be used instead of SponsorBlock, but SponsorBlock is recommended
|
||||||
|
|
||||||
|
- **`isMuted`**:
|
||||||
|
Default mute state of videos (`true` for muted, `false` for unmuted).
|
||||||
|
|
||||||
|
By adjusting these parameters, you can fine-tune the slideshow's behavior and appearance to suit your needs.
|
||||||
|
|
||||||
|
|
||||||
## How to install
|
## Installation
|
||||||
1. Download [spotlight.html]
|
1. Download [spotlight.html](/spotlight.html), [script.js](/script.js) and [styles.css](/styles.css)
|
||||||
|
|
||||||
2. Go to your ```jellyfin-web``` folder and create a folder named ```ui``` and drop ```spotlight.html, script.js and styles.css``` in that folder
|
2. Modify `script.js` like [explained above](#configuration-parameters) if necessary.
|
||||||
|
|
||||||
3. In your Jellyfin Dashboard, under ```API Keys``` create an API key for Spotlight, copy the key, and insert it as the value for ```token``` in ```script.js```. You can also set the corresponding values for list name, random selection, show it only on main page, show title, plot length, etc.
|
3. Go to your ```jellyfin-web``` folder and create a folder named ```featured``` and drop ```spotlight.html, script.js and styles.css``` in that folder
|
||||||
|
|
||||||
4. ```Important: Use Notepad++ for this``` In the jellyfin-web folder, open the file ```home-html.RANDOMSTRINGHERE.chunk.js```
|
4. ```Important: Use Notepad++ for this```\
|
||||||
|
In the jellyfin-web folder, open the file ```home-html.RANDOMSTRINGHERE.chunk.js```
|
||||||
|
|
||||||
5. Ctrl+F and search for ```data-backdroptype="movie,series,book">```
|
5. Ctrl+F and search for ```data-backdroptype="movie,series,book">```
|
||||||
|
|
||||||
6. Paste this after the >
|
6. Paste this after the >
|
||||||
|
|
||||||
```js
|
```js
|
||||||
<style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em;}} </style> <iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
<style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em; height: 25em;}} </style> <iframe class="featurediframe" src="/web/featured/spotlight.html"></iframe>
|
||||||
```
|
```
|
||||||
7. Save the file.
|
7. Save the file.
|
||||||
|
|
||||||
@@ -41,45 +120,25 @@ Testet on Jellyfin 10.10.0
|
|||||||
|
|
||||||
9. That's it.
|
9. That's it.
|
||||||
|
|
||||||

|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
Simply delete Step 7's snippet added to ```home-html.chunk.js``` then refresh your browser's cache. You can, but not have to delete the featured folder.
|
||||||
|
|
||||||
|
## How to feature specific content in the bar
|
||||||
|
|
||||||
|
By default, the bar will feature content at random as long as it is available to the current user and no `list.txt` is available or if it is empty `below` line 1.
|
||||||
|
|
||||||
|
In the first line, you can set a title for the featured bar, which can then be displayed. In addition, set MuteOn or MuteOff behind the title to control the behavoir of the trailer audio.
|
||||||
|
|
||||||
|
To preselect content, place a [list.txt](/list.txt) in the ```featured``` folder and paste the ID of each piece of content to be featured (IDs can be found in the address bar).
|
||||||
|
|
||||||
|
`IMPORTANT` If you use list.txt to preselect content and a User has an Age Rating limit on their account (U, PG. FSK etc) make sure you add content for them to see too, or it will just be blank (content above their Age Limit is hidden to them)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Desktop View
|
||||||
|

|
||||||
|
|
||||||
# Mobile View (Landscape / Portrait)
|
# Mobile View (Landscape / Portrait)
|
||||||

|

|
||||||
|
|
||||||
# How to feature specific content in the bar
|
|
||||||
|
|
||||||
By default, the bar will feature content at random as long as it is available to the current user.
|
|
||||||
|
|
||||||
To preselect content, place a [list.txt](link) in the ```ui``` folder and paste the ID of each piece of content to be featured (IDs can be found in the address bar).
|
|
||||||
|
|
||||||
# Uninstallation
|
|
||||||
|
|
||||||
Simply delete Step 5's snippet added to ```home-html.chunk.js``` then refresh your browser's cache.
|
|
||||||
|
|
||||||
# Fullscreen Version
|
|
||||||
|
|
||||||
No changes here from my side...
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Same as above except use [this version of spotlight.html](https://github.com/tedhinklater/Jellyfin-Featured-Content-Bar/blob/main/fullscreen/spotlight.html)
|
|
||||||
|
|
||||||
insert this into home-html.RANDOMSTRINGHERE.chunk.js after ```data-backdroptype="movie,series,book">```
|
|
||||||
|
|
||||||
```js
|
|
||||||
<style>.featurediframe { width: 100vw; height: 100vh; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 40px} @media (max-width:1000px) and (orientation:portrait) {.featurediframe {height: 46vh; width: 95vw;}} @media (max-width:1000px) and (orientation:landscape) {.featurediframe {height: 98vh; width: 95vw;}} @media (min-width: 2000px) { .featurediframe {height:102vh;}}</style><iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
|
||||||
```
|
|
||||||
|
|
||||||
and add this CSS to the very ```end``` of your Custom CSS
|
|
||||||
|
|
||||||
```css
|
|
||||||
.layout-desktop .page.homePage.libraryPage.allLibraryPage.backdropPage.pageWithAbsoluteTabs.withTabs.mainAnimatedPage { margin-top:-4.5em;}
|
|
||||||
.layout-desktop .overflowBackdropCard, .overflowSmallBackdropCard { width: 12.7vw !important; padding-right: 1.85em;}
|
|
||||||
.layout-desktop .skinHeader-withBackground {background-color: transparent; backdrop-filter: blur(0px);}
|
|
||||||
.layout-desktop #homeTab .section0 .sectionTitle.sectionTitle-cards.padded-left { display: none !important;}
|
|
||||||
.layout-desktop #homeTab .verticalSection.section1.emby-scroller-container { position: relative; top: -27em; left: 73em; width: 44vw; margin-bottom: -17em;}
|
|
||||||
.layout-desktop #homeTab .verticalSection.section2.emby-scroller-container::after { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100vw; background: black; z-index: -1;}
|
|
||||||
[dir="ltr"] #homeTab .verticalSection.section0.emby-scroller-container .emby-scrollbuttons {right: -5em; top: -2em;}
|
|
||||||
.layout-desktop #homeTab .verticalSection.section0 .cardText-first {display: none !important;}
|
|
||||||
.layout-desktop #homeTab .sections.homeSectionsContainer { margin-top: 2em;}
|
|
||||||
```
|
|
||||||
|
|||||||
140
README_GitHub.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# SpotlightTrailer - Featured Content Bar
|
||||||
|
Thanks to [SethBacon](https://forum.jellyfin.org/u-sethbacon) & [BobHasNoSoul](https://github.com/BobHasNoSoul) & [MakD](https://github.com/MakD) & [tedhinklater](https://github.com/tedhinklater) for their talents and work
|
||||||
|
|
||||||
|
## Main Differences in this fork
|
||||||
|
- automatically skip outros/endcards and intros on trailers using SponsorBlock API
|
||||||
|
- show the featured bar only at the main page, hide it in favorites/requests tab
|
||||||
|
- automatically switch between diffrent seasonal lists
|
||||||
|
- small ui style fixes
|
||||||
|
- html, css and javascript in seperate files
|
||||||
|
- display title for featured content (optional)
|
||||||
|
- **no hard coded api key needed**
|
||||||
|
|
||||||
|
Testet on Jellyfin 10.10.7
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [SpotlightTrailer - Featured Content Bar](#spotlighttrailer---featured-content-bar)
|
||||||
|
- [Main Differences in this fork](#main-differences-in-this-fork)
|
||||||
|
- [Table of Contents](#table-of-contents)
|
||||||
|
- [Configuration Parameters](#configuration-parameters)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Uninstall](#uninstall)
|
||||||
|
- [How to feature specific content in the bar](#how-to-feature-specific-content-in-the-bar)
|
||||||
|
- [Desktop View](#desktop-view)
|
||||||
|
- [Mobile View (Landscape / Portrait)](#mobile-view-landscape--portrait)
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Configuration Parameters
|
||||||
|
|
||||||
|
The following configuration parameters are used to customize the behavior and appearance of the slideshow. You can adjust them at the beginning of `script.js`:
|
||||||
|
|
||||||
|
- **`title`**:
|
||||||
|
The title of the slideshow. Set this to true to show the title from first line of `list.txt` (see [list.txt](/list.txt)).
|
||||||
|
|
||||||
|
- **`listFileName`**:
|
||||||
|
The name of the file containing the list of movie or series IDs. Ensure this file exists in the correct location.
|
||||||
|
|
||||||
|
- **`moviesSeriesBoth`**:
|
||||||
|
Specifies the type of content to display:
|
||||||
|
- `1` for movies only,
|
||||||
|
- `2` for series only,
|
||||||
|
- `3` for both.
|
||||||
|
|
||||||
|
Default is `3`
|
||||||
|
|
||||||
|
- **`shuffleInterval`**:
|
||||||
|
Time interval (in milliseconds) between slides, unless a trailer is playing. Adjust for desired slide transition speed.
|
||||||
|
|
||||||
|
- **`useTrailers`**:
|
||||||
|
Enable (`true`) or disable (`false`) the display of trailers in the slideshow.
|
||||||
|
|
||||||
|
- **`setRandomMovie`**:
|
||||||
|
Enable (`true`) or disable (`false`) random selection of movies or series from the list. Default is `true`
|
||||||
|
|
||||||
|
- **`showOnOtherPages`**:
|
||||||
|
Set to `true` to show the slideshow on additional pages, such as "Favorites" or "Requests." Default is `false`.
|
||||||
|
|
||||||
|
- **`disableTrailerControls`**:
|
||||||
|
Set to `true` to hide trailer controls; `false` enables user control over the trailer. Default is `true`
|
||||||
|
|
||||||
|
- **`setMutedHover`**:
|
||||||
|
Set to `false` to disable unmuting the video on hover
|
||||||
|
Default mute setting for trailers on hover over entire slideshow element:
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`umuteOnHover`**:
|
||||||
|
Unmute video when hovered over (`true`) or keep muted (`false`).
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`unmutedVolume`**:
|
||||||
|
Volume level (0–100) when unmuted.
|
||||||
|
Default is `20`
|
||||||
|
|
||||||
|
- **`useSponsorBlock`**:
|
||||||
|
Enable (`true`) or disable (`false`) the use of SponsorBlock data for skipping segments (outros, intros) in trailers.
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`skipIntro`**:
|
||||||
|
Enable (`true`) or disable (`false`) to skip the intro segment of the trailer.
|
||||||
|
Default is `true`
|
||||||
|
|
||||||
|
- **`plotMaxLength`**:
|
||||||
|
Maximum number of characters for plot descriptions. Adjust as needed.
|
||||||
|
|
||||||
|
- **`trailerMaxLength`**:
|
||||||
|
Maximum duration (in milliseconds) of trailers. Set to `0` to disable trailer length limitation. Could be used instead of SponsorBlock, but SponsorBlock is recommended
|
||||||
|
|
||||||
|
- **`isMuted`**:
|
||||||
|
Default mute state of videos (`true` for muted, `false` for unmuted).
|
||||||
|
|
||||||
|
By adjusting these parameters, you can fine-tune the slideshow's behavior and appearance to suit your needs.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. Download [spotlight.html](/spotlight.html), [script.js](/script.js) and [styles.css](/styles.css)
|
||||||
|
|
||||||
|
2. Modify `script.js` like [explained above](#configuration-parameters) if necessary.
|
||||||
|
|
||||||
|
3. Go to your ```jellyfin-web``` folder and create a folder named ```featured``` and drop ```spotlight.html, script.js and styles.css``` in that folder
|
||||||
|
|
||||||
|
4. ```Important: Use Notepad++ for this```\
|
||||||
|
In the jellyfin-web folder, open the file ```home-html.RANDOMSTRINGHERE.chunk.js```
|
||||||
|
|
||||||
|
5. Ctrl+F and search for ```data-backdroptype="movie,series,book">```
|
||||||
|
|
||||||
|
6. Paste this after the >
|
||||||
|
|
||||||
|
```js
|
||||||
|
<style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em; height: 25em;}} </style> <iframe class="featurediframe" src="/web/featured/spotlight.html"></iframe>
|
||||||
|
```
|
||||||
|
7. Save the file.
|
||||||
|
|
||||||
|
8. Empty your browser's cached web content (Ctrl+F5 or empty it from your browser's Cookies and Site Data settings section)
|
||||||
|
|
||||||
|
9. That's it.
|
||||||
|
|
||||||
|
|
||||||
|
## Uninstall
|
||||||
|
|
||||||
|
Simply delete Step 7's snippet added to ```home-html.chunk.js``` then refresh your browser's cache. You can, but not have to delete the featured folder.
|
||||||
|
|
||||||
|
## How to feature specific content in the bar
|
||||||
|
|
||||||
|
By default, the bar will feature content at random as long as it is available to the current user and no `list.txt` is available or if it is empty `below` line 1.
|
||||||
|
|
||||||
|
In the first line, you can set a title for the featured bar, which can then be displayed. In addition, set MuteOn or MuteOff behind the title to control the behavoir of the trailer audio.
|
||||||
|
|
||||||
|
To preselect content, place a [list.txt](/list.txt) in the ```featured``` folder and paste the ID of each piece of content to be featured (IDs can be found in the address bar).
|
||||||
|
|
||||||
|
`IMPORTANT` If you use list.txt to preselect content and a User has an Age Rating limit on their account (U, PG. FSK etc) make sure you add content for them to see too, or it will just be blank (content above their Age Limit is hidden to them)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Desktop View
|
||||||
|

|
||||||
|
|
||||||
|
# Mobile View (Landscape / Portrait)
|
||||||
|

|
||||||
52
SEASONAL_LISTS.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Seasonal Lists Documentation
|
||||||
|
|
||||||
|
This document explains how to use seasonal lists with the Featured Content Bar.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
To enable seasonal lists, set `useSeasonalLists = true` in the main script.js file.
|
||||||
|
|
||||||
|
## Seasonal Periods
|
||||||
|
|
||||||
|
The system automatically detects the following periods:
|
||||||
|
|
||||||
|
### Special Events (take precedence over seasons)
|
||||||
|
- **New Year**: January 1-7 → `newyear_list.txt`
|
||||||
|
- **Valentine's day**: February 10-20 → `valentine_list.txt`
|
||||||
|
- **Easter**: 1 week around Easter Sunday → `easter_list.txt`
|
||||||
|
- **Halloween**: October 20-31 → `halloween_list.txt`
|
||||||
|
|
||||||
|
### Regular Seasons
|
||||||
|
- **Spring**: March-May → `spring_list.txt`
|
||||||
|
- **Summer**: June-August → `summer_list.txt`
|
||||||
|
- **Autumn**: September-November → `autumn_list.txt`
|
||||||
|
- **Winter**: December-February → `winter_list.txt`
|
||||||
|
|
||||||
|
## List Files
|
||||||
|
|
||||||
|
Create the following files in your `featured` directory:
|
||||||
|
|
||||||
|
- `spring_list.txt` - Spring movies/shows
|
||||||
|
- `summer_list.txt` - Summer movies/shows
|
||||||
|
- `autumn_list.txt` - Autumn movies/shows
|
||||||
|
- `winter_list.txt` - Winter movies/shows
|
||||||
|
- `newyear_list.txt` - New Year themed content
|
||||||
|
- `valentine_list.txt` - Romance/Valentine themed content
|
||||||
|
- `easter_list.txt` - Easter/Family themed content
|
||||||
|
- `halloween_list.txt` - Horror/Halloween themed content
|
||||||
|
|
||||||
|
## File Format
|
||||||
|
|
||||||
|
Each seasonal list file follows the same format as the main `list.txt`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Title of List [muteon/muteoff]
|
||||||
|
movie_id_1
|
||||||
|
movie_id_2
|
||||||
|
series_id_1
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fallback
|
||||||
|
|
||||||
|
If seasonal lists are disabled or a seasonal file doesn't exist, the system will fall back to using the default `list.txt` file.
|
||||||
BIN
fullscreen/Library Cards/4k movies.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
fullscreen/Library Cards/4k shows.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
fullscreen/Library Cards/Anime.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
fullscreen/Library Cards/Books.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
fullscreen/Library Cards/Collections.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
fullscreen/Library Cards/Documentaries.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
fullscreen/Library Cards/Favourites.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
fullscreen/Library Cards/Folders.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
fullscreen/Library Cards/Live TV.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
fullscreen/Library Cards/Movies.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
fullscreen/Library Cards/Music Videos.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
fullscreen/Library Cards/Music.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
fullscreen/Library Cards/Photos.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
fullscreen/Library Cards/Series.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
fullscreen/Library Cards/TV Shows.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
1
fullscreen/Library Cards/card
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
9
fullscreen/fullscreenspotlight.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.layout-desktop .page.homePage.libraryPage.allLibraryPage.backdropPage.pageWithAbsoluteTabs.withTabs.mainAnimatedPage { margin-top:-4.5em;}
|
||||||
|
.layout-desktop .overflowBackdropCard, .overflowSmallBackdropCard { width: 12.7vw !important; padding-right: 1.85em;}
|
||||||
|
.layout-desktop .skinHeader-withBackground {background-color: transparent; backdrop-filter: blur(0px);}
|
||||||
|
.layout-desktop #homeTab .section0 .sectionTitle.sectionTitle-cards.padded-left { display: none !important;}
|
||||||
|
.layout-desktop #homeTab .verticalSection.section1.emby-scroller-container { position: relative; top: -27em; left: 73em; width: 44vw; margin-bottom: -17em;}
|
||||||
|
.layout-desktop #homeTab .verticalSection.section2.emby-scroller-container::after { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100vw; background: black; z-index: -1;}
|
||||||
|
[dir="ltr"] #homeTab .verticalSection.section0.emby-scroller-container .emby-scrollbuttons {right: -5em; top: -2em;}
|
||||||
|
.layout-desktop #homeTab .verticalSection.section0 .cardText-first {display: none !important;}
|
||||||
|
.layout-desktop #homeTab .sections.homeSectionsContainer { margin-top: 2em;}
|
||||||
1
fullscreen/home-html snippet
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<style>.featurediframe { width: 100vw; height: 100vh; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 40px} @media (max-width:1000px) and (orientation:portrait) {.featurediframe {height: 46vh; width: 95vw;}} @media (max-width:1000px) and (orientation:landscape) {.featurediframe {height: 98vh; width: 95vw;}} @media (min-width: 2000px) { .featurediframe {height:102vh;}}</style><iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
||||||
476
fullscreen/spotlight.html
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||||
|
<title>Jellyfin Spotlight v2.3.2</title>
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<style>
|
||||||
|
@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');
|
||||||
|
body { margin: 0; padding: 0; overflow: hidden; }
|
||||||
|
.slide { position: relative; width: 100vw; height: 100vh; box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);}
|
||||||
|
.slide:focus { outline: 2px solid #fff; }
|
||||||
|
.backdrop { position: absolute; top: 3.15em; left: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 30%; z-index: 1; transition: width 0.5s ease, filter 0.8s ease, scale 2s ease; transform-origin: top; animation: none;}
|
||||||
|
.logo { position: relative; transform: translateX(-50%) translateY(-50%); top: 56%; left: 14.5%; z-index: 3; text-shadow: -2px 2px 4px rgba(0, 0, 0, 0.5); filter: drop-shadow(1px 1px 1px); pointer-events: none; transition: transform 0.3s ease, max-height 0.3s ease, max-width 0.3s ease;}
|
||||||
|
.heading { position: absolute; top: 0; left: 0; width: 100%; height: 2.3em; background-color: transparent; font-family: "Titillium Web", sans-serif; color: #D3D3D3; font-size: 22px; display: flex; align-items: center; justify-content: flex-start; z-index: 2; padding: 10px; padding-left: 0px; box-sizing: border-box; text-shadow: 1px 1px 4px rgba(0, 0, 0, 1); }
|
||||||
|
.next-button { position: absolute; top: 0.4em; right: 0.25em; font-size: 1.5em; color: #D3D3D3; cursor: pointer; z-index: 10; font-family: "Titillium Web", sans-serif; font-size: 1.35em; text-shadow: 1px 1px 4px rgba(0, 0, 0, 1);}
|
||||||
|
.skip-button {font-size: 1.62em; top: 0.5em; opacity: 0.5; transition: opacity 0.3s ease, background 0.3s ease; padding-left: 0.15em; padding-right: 0.15em; padding-top: 0.1em; border-radius: 50%; box-sizing: border-box; font-style: normal; font-weight: 400; letter-spacing: normal; line-height: 1; text-transform: none; word-wrap: normal; direction: inherit; white-space: nowrap; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; -moz-osx-font-smoothing: grayscale; -webkit-font-feature-settings: "liga"; font-feature-settings: "liga";}
|
||||||
|
.skip-button:hover {background: #ffffff70; opacity: 1;}
|
||||||
|
.video-container { position: absolute; right: 0; top: 50px; width: 0; height: calc(100% - 50px); overflow: hidden; z-index: 2; transition: width 0.5s ease; z-index: 6; border-top-right-radius: 0.5em; border-bottom-right-radius: 0.5em; transform-origin: right;}
|
||||||
|
.video-player { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 7; }
|
||||||
|
.clickable-overlay { position: absolute; top: 50px; left: 0; width: 100%; height: calc(100%); z-index: 2; pointer-events: none; background: transparent; transition: background 0.8s ease, backdrop-filter 0.8s ease;}
|
||||||
|
.clickable-overlay::before { content: ''; position: absolute; top: 25%; left: 0; width: 100%; height: 75%; background: linear-gradient(0deg, rgb(0% 0% 0%) 0%, rgba(0, 0, 0, 0.99) 6.25%, rgba(0, 0, 0, 0.96) 12.5%, rgba(0, 0, 0, 0.92) 18.75%, rgba(0, 0, 0, 0.86) 25%, rgba(0, 0, 0, 0.78) 31.25%, rgba(0, 0, 0, 0.69) 37.5%, rgba(0, 0, 0, 0.6) 43.75%, rgba(0, 0, 0, 0.5) 50%, rgba(0, 0, 0, 0.4) 56.25%, rgba(0, 0, 0, 0.31) 62.5%, rgba(0, 0, 0, 0.22) 68.75%, rgba(0, 0, 0, 0.14) 75%, rgba(0, 0, 0, 0.08) 81.25%, rgba(0, 0, 0, 0.04) 87.5%, rgba(0, 0, 0, 0.01) 93.75%, rgba(0, 0, 0, 0) 100% ); opacity: 0.15; transition: opacity 0.3s ease; z-index: 1;}
|
||||||
|
.clickable-overlay::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(180deg, rgb(0% 0% 0% / 0.9) 0%, rgb(0% 0% 0% / 0.6952285766601562) 6.25%, rgb(0% 0% 0% / 0.5275634765625) 12.5%, rgb(0% 0% 0% / 0.39222564697265627) 18.75%, rgb(0% 0% 0% / 0.284765625) 25%, rgb(0% 0% 0% / 0.20106353759765627) 31.25%, rgb(0% 0% 0% / 0.1373291015625) 37.5%, rgb(0% 0% 0% / 0.09010162353515627) 43.75%, rgb(0% 0% 0% / 0.05625000000000002) 50%, rgb(0% 0% 0% / 0.03297271728515627) 56.25%, rgb(0% 0% 0% / 0.017797851562500022) 62.5%, rgb(0% 0% 0% / 0.00858306884765625) 68.75%, rgb(0% 0% 0% / 0.003515625000000022) 75%, rgb(0% 0% 0% / 0.0011123657226562722) 81.25%, rgb(0% 0% 0% / 0.0002197265625000222) 87.5%, rgb(0% 0% 0% / 0.000013732910156272204) 93.75%, rgb(0% 0% 0% / 0) 100% ); opacity: 0.25; transition: opacity 0.3s ease, visibility 0.8s ease; z-index: 3; border-radius: 0.25em; }
|
||||||
|
.lorem-ipsum {position: relative; bottom: -44em; left: 59em; text-align: left; font-family: "Titillium Web", sans-serif; font-size: 92%; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); opacity: 1; color: #fff; z-index: 4; box-sizing: border-box; background: transparent; max-width: fit-content; padding-left: 0.3em; padding-right: 0.3em; display: flex; flex-direction: row; max-height: 1.9em; padding-top: 0.15em; white-space: nowrap;}
|
||||||
|
.lorem-ipsum span { margin-right: 0.2em;}
|
||||||
|
.age-rating {position: absolute; top: 3.5em; left: 0em; text-align: left; font-family: "Titillium Web", sans-serif; font-size: 95%; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); opacity: 1; color: #fff; z-index: 4; box-sizing: border-box; background: transparent; max-width: fit-content; padding-left: 0.3em; padding-right: 0.3em; display: flex; flex-direction: row; max-height: 1.9em; padding-top: 0.15em; white-space: nowrap;}
|
||||||
|
.plot { position: absolute; bottom: 0; left: -0.25em; right: 0; color: #fff; font-family: "Titillium Web", sans-serif; font-size: 0.9em; font-weight: 500; background: transparent; padding: 10px; z-index: 7; box-sizing: border-box; padding-bottom: 3px; padding-top: 0.5em; line-height: 1.2em; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; border-bottom-left-radius: 0.45em; transition: opacity 0.1s ease, max-height 0.8s ease; opacity: 0; max-width: 31%; opacity: 1; -webkit-line-clamp: 3; max-height: 7.8em;}
|
||||||
|
.genres { position: relative; bottom: -44.1em; color: #fff; z-index: 2; font-family: "Titillium Web", sans-serif; font-size: 0.79em; opacity: 1; transition: opacity 0.3s ease; max-width: 39em; overflow: hidden; height: 1.95em; padding-top: 0.25em; line-height: 2em; white-space: nowrap;}
|
||||||
|
.premiere-year {position: absolute; top: 5.75em; left: 0.5em; font-size: 0.9em; font-family: "Titillium Web", sans-serif; color: #fff; z-index: 3; opacity: 0; transition: opacity 0.3s; text-align: center; width: 3.5em;}
|
||||||
|
.additional-info {position: absolute; top: 7.3em; left: 31em; font-size: 0.9em; font-family: "Titillium Web", sans-serif; color: #fff; z-index: 3; opacity: 1; transition: opacity 0.3s; text-align: right; width: 5em;}
|
||||||
|
.community-rating {position: absolute; top: 5.75em; right: 69.5%; font-size: 0.9em; font-family: "Titillium Web", sans-serif; color: #fff; z-index: 3; opacity: 1; transition: opacity 0.3s; text-align: center; width: 3.6em; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.critic-rating {position: absolute; top: 5.75em; right: 73%; font-size: 0.9em; font-family: "Titillium Web", sans-serif; color: #fff; z-index: 3; opacity: 1; transition: opacity 0.3s; text-align: center; width: 3.9em; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
@keyframes objectPositionAnimation { 0% { object-position: center 30%; } 40% { object-position: center 80%; } 80% { object-position: center 20%; } 100% { object-position: center 30%; }}
|
||||||
|
.star-icon { color: gold; font-size: 0.9em; padding-right: 0.2em; margin-left: -0.1em;}
|
||||||
|
@media (max-width: 1000px) { .age-rating {position: absolute; top: 3em; left: 0em; font-size: 0.7em;} .lorem-ipsum {font-size: 0.7em; transform: translateX(50%); right: 50vw !important; padding-left: 0; padding-right: 1.5em;} .lorem-ipsum span {background:transparent !important; border: none !important;}.logo { transform: translateX(-50%) translateY(-50%); top: 10.5em; left: 50% !important; max-width: 70%; max-height: 45%; transform-origin: 50% 50%;} .plot {display: none; opacity: 1 !important; font-size: 90%; bottom: 0.5em; -webkit-line-clamp: 2; max-height: 3.3em; overflow: hidden; width: 100% !important; max-width: 100%; line-height: 1.45em;} .lorem-ipsum::before { opacity:0; padding-bottom: 0.25em; } .clickable-overlay { background: linear-gradient(0deg, rgb(0% 0% 0%) 0%, rgb(0% 0% 0% / 0.9990234375) 6.25%, rgba(0, 0, 0, 0.99) 12.5%, rgba(0, 0, 0, 0.97) 18.75%, rgba(0, 0, 0, 0.94) 25%, rgba(0, 0, 0, 0.88) 31.25%, rgba(0, 0, 0, 0.79) 37.5%, rgba(0, 0, 0, 0.67) 43.75%, rgba(0, 0, 0, 0.5) 50%, rgba(0, 0, 0, 0.33) 56.25%, rgba(0, 0, 0, 0.21) 62.5%, rgba(0, 0, 0, 0.12) 68.75%, rgba(0, 0, 0, 0.06) 75%, rgba(0, 0, 0, 0.03) 81.25%, rgba(0, 0, 0, 0.01) 87.5%, rgba(0, 0, 0, 0) 93.75%, rgba(0, 0, 0, 0) 100% ); opacity: 0.8;} .backdrop {left: 0% !important; width: 100% !important; top: 2.95em;} .lorem-ipsum .full-content { opacity: 1; max-width: 100%;} .lorem-ipsum::before { opacity:1; backdrop-filter: blur(2px); transform: translateX(0);} .clickable-overlay::before { opacity: 0.98; backdrop-filter: blur(0px);} .slide:hover .clickable-overlay::before { opacity: 0.98;} .skip-button {opacity: 1 !important; font-size: 0.9em; top: 1.1em; color: #fff;} .genres {bottom: 4em; left: unset !important; overflow: hidden; max-width: 100vw !important; white-space: nowrap; left: 0; z-index: 8; color: #fff; font-size: 0.7em;} .genres span {background: transparent !important; border: none !important;} .additional-info {left: 18.5%; top: 5em; text-align: right;} .community-rating {left: 86vw !important; top: 19.45em !important; text-align: left; z-index: 8; font-size: 0.85em !important;} .critic-rating {font-size: 0.85em !important; left: 70vw !important; top: 19.45em !important; z-index: 8;} .additional-info {left: 18.5%; top: 5em; text-align: left;} .heading {z-index: 0;} .heading::before {display: none;} .video-container {max-width: 0 !important;} .watch-trailer-button {position: absolute; border: none; border-radius: 5px; padding: 0.5em 1em; padding-right: 1.5em; font-family: "Titillium Web", sans-serif; cursor: pointer; z-index: 10; transition: background-color 0.3s ease; align-items: center; justify-content: center; display: flex !important; top: unset !important; bottom: 0.4em; font-size: 0.7em !important; background: #fff9 !important; color: #000e !important; font-weight: 600; transform: translateX(-100%) !important; left: 47% !important;} .slide { box-shadow: none;} .text-container::before { display: none !important;} .buttons-container {top: unset !important; display: flex; position: absolute; bottom: 0.6em; left: 0em !important; width: 100vw; z-index: 10;} .details-button { background: #fff9 !important; color: 000e !important; position: absolute; border: none; border-radius: 5px; padding: 0.5em 1.5em; font-family: "Titillium Web", sans-serif; cursor: pointer; z-index: 10; transition: background-color 0.3s ease; align-items: center; justify-content: center; bottom: -0.4em; font-size: 0.7em !important; background: #fff9; color: #000e; font-weight: 600; transform: translateX(100%) !important; right: 47%; display: flex; padding-right: 2em;} .details-button:hover { background-color: #fffc;} .watch-trailer-button:hover { background-color: #fffc !important;} .details-button:hover { background-color: #fffc !important;} .fas {padding: 0.5em;}}
|
||||||
|
@media (max-width:1000px) and (orientation:portrait) {.back-button {right:58vw;} .skip-button {right: 0em; top: 0.25em !important;} .genres {right: 50vw; position: absolute; max-width: 95vw !important; overflow:hidden; top: unset; transform: translateX(50%); bottom: 6.5em;} .lorem-ipsum {position: absolute; left: unset; bottom: 4.25em;}}
|
||||||
|
@media (max-width:1000px) and (orientation:landscape) {.community-rating {left:89vw !important;} .critic-rating {left:76vw !important;} .back-button {right:70vw;} .skip-button {right: 0em; top: 0.25em !important;} .genres {position: absolute; bottom: 6.5em; top: unset; transform: translateX(50%); right: 50vw;} .lorem-ipsum {position: absolute; left: unset; bottom: 4.25em;}}
|
||||||
|
@media (min-width: 1001px) { .material-icons {font-size: 2em;}.details-button { color: white; background: #101010e3; font-size: 1.75em; padding: 0.25em; padding-left: 0.5em; padding-right: 0.75em; transition: background-color 0.3s ease; cursor: pointer; border-radius: 0.5em;} .lorem-ipsum { bottom: -47em; left: 59em; font-size: 1.1em; transform: translateX(-100%); transition: left 0.5s;} clickable-overlay {pointer-events: none;} .logo {max-height: 23em; position: absolute; top: 29em; transition: transform 0.1s ease, max-height 0.3s ease; max-width: 47em; left: 34em; transform: translateX(-50%) translateY(-50%); transition: left 0.5s, max-width 0.5s;} @media screen and (max-width:1400px){ .logo {max-width: 37em; left: 30em;} .lorem-ipsum {left: 55em;}} .watch-trailer-button {display: none;} .backdrop {filter: brightness(95%) contrast(105%) saturate(105%); left: 0%; width: 100%; transition: width 0.5s ease, filter 0.8s ease, transform 2s ease; filter: brightness(95%) contrast(105%) saturate(105%);} .slide {margin-top: -3.15em; height: 100vh;} .skip-button {top: 17.5em; right: 0.5em; font-size: 2em;} .video-container {top: 9em; height: 18.62em; right: 4em; border-radius: 0em; transform-origin: 100% 0; transition: transform 0.5s ease; transition-delay: 1s;} .plot {position: relative; bottom: -33.5em; left: 4em; font-size: 1.25em; max-width: 49em !important; width: 49em !important; -webkit-line-clamp: 3;} .genres {z-index: 3; bottom: unset; top: 10.5em; left: 5.1em; font-size: 1.1em; max-width: 74em;} .age-rating {font-size: 1.3em !important; position: absolute; left: 4em; top: 6.2em; white-space: nowrap;} .community-rating {top: 44em; left: 50em; font-size: 1.2em;} .critic-rating {top: 44em; left: 43em; font-size: 1.2em;} .heading::before {content: ''; position: absolute; top: 2.27em; left: 0; width: 100%; background: linear-gradient(0deg, rgb(0% 0% 0%) 0%, rgb(0% 0% 0% / 0.9903926402016152) 6.25%, rgb(0% 0% 0% / 0.9619397662556434) 12.5%, rgb(0% 0% 0% / 0.9157348061512727) 18.75%, rgb(0% 0% 0% / 0.8535533905932737) 25%, rgb(0% 0% 0% / 0.7777851165098011) 31.25%, rgb(0% 0% 0% / 0.6913417161825449) 37.5%, rgb(0% 0% 0% / 0.5975451610080642) 43.75%, rgb(0% 0% 0% / 0.5) 50%, rgb(0% 0% 0% / 0.4024548389919359) 56.25%, rgb(0% 0% 0% / 0.3086582838174552) 62.5%, rgb(0% 0% 0% / 0.22221488349019902) 68.75%, rgb(0% 0% 0% / 0.14644660940672627) 75%, rgb(0% 0% 0% / 0.08426519384872733) 81.25%, rgb(0% 0% 0% / 0.03806023374435663) 87.5%, rgb(0% 0% 0% / 0.009607359798384785) 93.75%, rgb(0% 0% 0% / 0) 100% ); height: 100vh; z-index:6; opacity: 1; bottom: 0;} .skip-button { position: absolute; top: 0.4em; right: 0.25em; font-size: 1.5em; color: #D3D3D3; cursor: pointer; z-index: 10; font-family: "Titillium Web", sans-serif; font-size: 1.35em; text-shadow: 1px 1px 4px rgba(0, 0, 0, 1);}}
|
||||||
|
@media (max-width: 500px) { .community-rating {left: 0em; top: 5.6em;} .critic-rating {left: 0em; top: 7.6em;} .additional-info {left: 18.5%; top: 5em; text-align: right;} .heading::before {display: none;} .logo {max-width: 60%; max-height: 35%; left: 50% !important;}}
|
||||||
|
.age-rating { font-weight: 600; font-size: 1.1em;} .additional-info {display: none;}
|
||||||
|
.age-rating span.gb-u, .age-rating span.u { background-color: #078c6d; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.gb-pg, .age-rating span.pg { background-color: #d7a203; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.gb-12a, .age-rating span.\31 2A { background-color: #ee7600; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.gb-12, .age-rating span.\31 2, .age-rating span.gb-15, .age-rating span.\31 2 { background-color: #e19887; color: #e2002d; border: 0.09em solid white !important; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.pg-13 { background-color: #157c0d; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.tv-y7, .age-rating span.tv-13, .age-rating span.tv-14, .age-rating span.\31 6, .age-rating span.tv-ma, .age-rating span.tv-y, .age-rating span.tv-g, .age-rating span.tv-pg { background-color: #157c0d; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.nc-17, .age-rating span.gb-18, .age-rating span.\31 8, .age-rating span.r { background-color: #c10f1f; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1);}
|
||||||
|
.age-rating span.au-g, .age-rating span.nz-g, .age-rating span.eu-pg, .age-rating span.ca-g, .age-rating span.jp-g, .age-rating span.de-0 { background-color: #078c6d; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); }
|
||||||
|
.age-rating span.au-pg, .age-rating span.nz-pg, .age-rating span.eu-12, .age-rating span.ca-pg, .age-rating span.jp-pg12, .age-rating span.de-6 { background-color: #d7a203; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); }
|
||||||
|
.age-rating span.au-m, .age-rating span.nz-m, .age-rating span.eu-16, .age-rating span.ca-14a, .age-rating span.jp-r15, .age-rating span.de-12 { background-color: #07b1e0; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); }
|
||||||
|
.age-rating span.au-ma15, .age-rating span.nz-r15, .age-rating span.eu-18, .age-rating span.ca-18a, .age-rating span.jp-r18, .age-rating span.de-16 { background-color: #fc9712; color: white; border: 0.09em solid white !important; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); }
|
||||||
|
.age-rating span.au-r18, .age-rating span.nz-r18, .age-rating span.ca-r, .age-rating span.jp-r18+, .age-rating span.de-18 { background-color: #c10f1f; color: white; text-shadow: 1px 1px 1px rgba(0, 0, 0, 1); }
|
||||||
|
.video-container {overflow: visible;}
|
||||||
|
.watch-trailer-button { }
|
||||||
|
.watch-trailer-button .fa-play { margin-right: 0.5em;}
|
||||||
|
.slide { opacity: 1; transition: opacity 0.31s ease-in-out, font-size 1s ease; }
|
||||||
|
.fade-in { opacity: 0; visibility: hidden; transition: opacity 0.31s ease-in-out;}
|
||||||
|
.fade-in-active { opacity: 1; visibility: visible;}
|
||||||
|
.fade-out { opacity: 0;}
|
||||||
|
.backdrop { transition: opacity 0.3s ease-in-out, width 0.5s ease, transform 1.5s ease, filter 1s ease;}
|
||||||
|
#video-overlay { position: fixed; top: 13vh; left: 0vw; width: 100%; height: 140vh; background-color: rgb(0, 0, 0); z-index: 1000; display: flex; background-size: cover; padding: 6vh; padding-top: 10vh; margin-top: -10vh;}
|
||||||
|
#video-overlay-content { position: relative; width: 90%; max-width: 100%; height: 60%; background-color: #000;}
|
||||||
|
#close-overlay { position: absolute; top: -1em; right: 0em; font-size: 1.5em; color: #7b7b7b; cursor: pointer;}
|
||||||
|
#countdown-bar-container { display: none; position: absolute; left: 0%; bottom: 0; width: 110%; height: 0.15em !important; background-color: rgba(0,0,0,0); z-index: 10;}
|
||||||
|
#countdown-bar { height: 100%; width: 0%; background-color: #ffffff; opacity: 0.5; transition: width 0.1s linear; border-radius: 1em;}
|
||||||
|
.video-player { mask-image: linear-gradient(45deg, rgb(0% 0% 0% / 0) 0%, rgb(0% 0% 0% / 0.1) 25%, rgb(0% 0% 0% / 0.7) 50%, rgb(0% 0% 0% / 1) 100% ); -webkit-mask-image: linear-gradient(45deg, rgb(0% 0% 0% / 0) 0%, rgb(0% 0% 0% / 0.1) 25%, rgb(0% 0% 0% / 0.7) 50%, rgb(0% 0% 0% / 1) 100% ); transition: mask-image 5s ease, -webkit-mask-image 5s ease; }
|
||||||
|
.video-container:hover { mask-image: none; -webkit-mask-image: none; transform:scale(1.335); transition-delay: 0s;}
|
||||||
|
.video-container:hover .video-player { mask-image: none; -webkit-mask-image: none;}
|
||||||
|
.skip-button {font-size: 2em; top: 50vh; position: absolute; z-index: 20; cursor: pointer; opacity: 0.5; transition: opacity 0.3s ease;} .skip-button:hover {opacity: 1;}
|
||||||
|
.buttons-container {display: flex; position: absolute; top: 53.6em; left: 8em; z-index: 10;}
|
||||||
|
.details-button:hover { background-color: #444;}
|
||||||
|
@media (min-width: 2000px) { .slide {font-size: 0.85vw;} .backdrop {top: 2em;}}
|
||||||
|
@media (min-width: 3000px) { .slide {.backdrop {top: 0em;} .slide {margin-top: -4vh;}}
|
||||||
|
@media screen and (min-aspect-ratio: 21/9) { .slide {font-size: 1.6vh; }}
|
||||||
|
.watch-trailer-button { position: absolute; border: none; border-radius: 5px; padding: 0.5em 1.5em; font-size: 0.9em; font-family: "Titillium Web", sans-serif; cursor: pointer; z-index: 10; transition: background-color 0.3s ease; align-items: center; justify-content: center; padding-right: 2em;}
|
||||||
|
.material-symbols-outlined { vertical-align: middle; font-size: 0.85em; opacity: 0.75; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="slides-container"></div>
|
||||||
|
<div id="video-overlay" style="display: none;">
|
||||||
|
<div id="video-overlay-content">
|
||||||
|
<span id="close-overlay" class="fas fa-times"></span>
|
||||||
|
<iframe id="trailer-video" width="100%" height="100%" frameborder="0" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let title = 'Spotlight', moviesSeriesBoth = 3, shuffleInterval = 15000, plotMaxLength = 350, token = 'YOURAPIKEYHERE', useTrailers = true;
|
||||||
|
let isChangingSlide = false, player = null, slideChangeTimeout = null, isHomePageActive = false;
|
||||||
|
let currentLocation = window.top.location.href;
|
||||||
|
let movieList = [], currentMovieIndex = 0;
|
||||||
|
|
||||||
|
const createElem = (tag, className, textContent, src, alt) => {
|
||||||
|
const elem = document.createElement(tag);
|
||||||
|
if (className) elem.className = className;
|
||||||
|
if (textContent) elem.textContent = textContent;
|
||||||
|
if (src) elem.src = src;
|
||||||
|
if (alt) elem.alt = alt;
|
||||||
|
return elem;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for screen size below 1000px
|
||||||
|
function isMobile() {
|
||||||
|
return window.innerWidth <= 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
const truncateText = (text, maxLength) => text.length > maxLength ? text.substr(0, maxLength) + '...' : text;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
if (player) { player.destroy(); player = null; }
|
||||||
|
clearTimeout(slideChangeTimeout);
|
||||||
|
const container = document.getElementById('slides-container');
|
||||||
|
if (container) container.innerHTML = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSlideElement = (movie, hasVideo = false) => {
|
||||||
|
cleanup();
|
||||||
|
const container = document.getElementById('slides-container');
|
||||||
|
const slide = createElem('div', 'slide');
|
||||||
|
['backdrop', 'logo'].forEach(type => slide.appendChild(createElem('img', type, null, `/Items/${movie.Id}/Images/${type.charAt(0).toUpperCase() + type.slice(1)}${type === 'backdrop' ? '/0' : ''}`, type)));
|
||||||
|
slide.appendChild(createElem('div', 'heading', title));
|
||||||
|
|
||||||
|
const textContainer = createElem('div', 'text-container');
|
||||||
|
const premiereYear = movie.PremiereDate ? new Date(movie.PremiereDate).getFullYear() : 'Unknown';
|
||||||
|
const additionalInfo = movie.Type === 'Series' ?
|
||||||
|
(movie.ChildCount ? `${movie.ChildCount} Season${movie.ChildCount > 1 ? 's' : ''}` : 'Unknown Seasons') :
|
||||||
|
(movie.RunTimeTicks ? `${Math.round(movie.RunTimeTicks / 600000000)} min` : 'Unknown Runtime');
|
||||||
|
|
||||||
|
let loremText = `
|
||||||
|
<span style="border: 1px solid #fff8; background: #101010e3; border-radius: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">${additionalInfo}</span>
|
||||||
|
<span style="border: 1px solid #fff8; background: #101010e3; border-radius: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">${premiereYear}</span> `;
|
||||||
|
|
||||||
|
if (movie.CommunityRating) {
|
||||||
|
loremText += `<span style="border: 1px solid #fff8; background: #101010e3; border-radius: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">
|
||||||
|
<i class="star-icon fas fa-star"></i> ${movie.CommunityRating.toFixed(1)}
|
||||||
|
</span> `;
|
||||||
|
}
|
||||||
|
if (movie.CriticRating) {
|
||||||
|
loremText += `<span style="border: 1px solid #fff8; background: #101010e3; border-radius: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">
|
||||||
|
<img src="https://i.imgur.com/rMvyQMt.png" alt="Rotten Tomatoes" style="width: 1.05em; height: 1.25em; font-size: 0.9em; padding-right: 0.1em; margin-left: -0.1em; vertical-align: middle;">
|
||||||
|
${movie.CriticRating}%</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Age Rating
|
||||||
|
|
||||||
|
const ageRating = movie.OfficialRating ? movie.OfficialRating : 'NR';
|
||||||
|
const ratingClass = ageRating.toLowerCase().replace(/ /g, '-');
|
||||||
|
|
||||||
|
const ageRatingDiv = createElem('div', 'age-rating');
|
||||||
|
const ageRatingSpan = createElem('span', ratingClass, ageRating);
|
||||||
|
ageRatingSpan.style.cssText = 'border: 0.09em solid currentColor; border-radius: .1em; padding: 0.2em; display: inline-block; text-align: center; line-height: 0.8em;';
|
||||||
|
ageRatingDiv.appendChild(ageRatingSpan);
|
||||||
|
slide.appendChild(ageRatingDiv);
|
||||||
|
|
||||||
|
// Genres Section
|
||||||
|
const genresDiv = createElem('div', 'genres');
|
||||||
|
if (movie.Genres && movie.Genres.length > 0) {
|
||||||
|
movie.Genres.forEach(genre => {
|
||||||
|
const genreElem = createElem('span', 'genre-item');
|
||||||
|
genreElem.textContent = genre;
|
||||||
|
genreElem.style.backgroundColor = '#101010e3';
|
||||||
|
genreElem.style.paddingLeft = '0.5em';
|
||||||
|
genreElem.style.paddingRight = '0.5em';
|
||||||
|
genreElem.style.paddingTop = '0.1em';
|
||||||
|
genreElem.style.paddingBottom = '0.1em';
|
||||||
|
genreElem.style.color = 'white';
|
||||||
|
genreElem.style.borderRadius = '0em';
|
||||||
|
genreElem.style.marginRight = '1em';
|
||||||
|
genreElem.style.border = '1px solid #fff8';
|
||||||
|
genreElem.style.borderRadius = '0.5em';
|
||||||
|
genresDiv.appendChild(genreElem);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
genresDiv.textContent = 'Genres: N/A';
|
||||||
|
}
|
||||||
|
slide.appendChild(genresDiv);
|
||||||
|
|
||||||
|
const loremDiv = createElem('div', 'lorem-ipsum');
|
||||||
|
loremDiv.innerHTML = loremText;
|
||||||
|
textContainer.appendChild(loremDiv);
|
||||||
|
textContainer.appendChild(createElem('div', 'plot', truncateText(movie.Overview, plotMaxLength)));
|
||||||
|
slide.appendChild(textContainer);
|
||||||
|
|
||||||
|
// Create Details buttons
|
||||||
|
const buttonsContainer = createElem('div', 'buttons-container');
|
||||||
|
|
||||||
|
// Details Button
|
||||||
|
const detailsButton = createElem('button', 'details-button');
|
||||||
|
const playIcon = createElem('i', 'fas fa-play');
|
||||||
|
detailsButton.appendChild(playIcon);
|
||||||
|
detailsButton.appendChild(document.createTextNode(' Play'));
|
||||||
|
detailsButton.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
window.top.location.href = `/#!/details?id=${movie.Id}`; // Navigate to details page
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append buttons to the container
|
||||||
|
buttonsContainer.appendChild(detailsButton);
|
||||||
|
slide.appendChild(buttonsContainer);
|
||||||
|
|
||||||
|
const backButton = createElem('div', 'back-button');
|
||||||
|
const backIcon = createElem('i', 'material-icons');
|
||||||
|
backIcon.textContent = 'chevron_left';
|
||||||
|
const skipButton = createElem('div', 'skip-button');
|
||||||
|
const skipIcon = createElem('i', 'material-icons');
|
||||||
|
skipIcon.textContent = 'chevron_right';
|
||||||
|
|
||||||
|
backButton.appendChild(backIcon);
|
||||||
|
skipButton.appendChild(skipIcon);
|
||||||
|
|
||||||
|
skipIcon.onclick = (e) => { e.stopPropagation(); fetchRandomMovie(); };
|
||||||
|
slide.appendChild(backButton);
|
||||||
|
slide.appendChild(skipButton);
|
||||||
|
|
||||||
|
const overlay = createElem('div', 'clickable-overlay');
|
||||||
|
overlay.onclick = () => window.top.location.href = `/#!/details?id=${movie.Id}`;
|
||||||
|
slide.appendChild(overlay);
|
||||||
|
|
||||||
|
if (hasVideo && movie.RemoteTrailers && movie.RemoteTrailers.length > 0) {
|
||||||
|
const trailerUrl = movie.RemoteTrailers[0].Url;
|
||||||
|
const watchTrailerButton = createElem('button', 'watch-trailer-button');
|
||||||
|
const playIcon = document.createElement('i');
|
||||||
|
playIcon.className = 'fas fa-film';
|
||||||
|
watchTrailerButton.appendChild(playIcon);
|
||||||
|
watchTrailerButton.appendChild(document.createTextNode(' Trailer'));
|
||||||
|
|
||||||
|
watchTrailerButton.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (isMobile()) {
|
||||||
|
// Show the video in an overlay for mobile
|
||||||
|
showVideoOverlay(trailerUrl);
|
||||||
|
} else {
|
||||||
|
// Open the trailer in a new tab for desktop
|
||||||
|
window.open(trailerUrl, '_blank');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
slide.appendChild(watchTrailerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useTrailers && hasVideo && movie.RemoteTrailers?.length > 0) {
|
||||||
|
const videoId = new URL(movie.RemoteTrailers[0].Url).searchParams.get('v');
|
||||||
|
const videoContainer = createElem('div', 'video-container');
|
||||||
|
const videoElement = createElem('div', 'video-player');
|
||||||
|
videoContainer.appendChild(videoElement);
|
||||||
|
slide.appendChild(videoContainer);
|
||||||
|
|
||||||
|
player = new YT.Player(videoElement, {
|
||||||
|
height: '100%', width: '100%', videoId,
|
||||||
|
events: {
|
||||||
|
'onReady': event => {
|
||||||
|
event.target.playVideo();
|
||||||
|
['backdrop', 'plot', 'lorem-ipsum'].forEach(cls => {
|
||||||
|
const element = document.querySelector(`.${cls}`);
|
||||||
|
if (element) element.style.width = '100%';
|
||||||
|
});
|
||||||
|
videoContainer.style.width = '27.6vw';
|
||||||
|
},
|
||||||
|
'onStateChange': event => { if (event.data === YT.PlayerState.ENDED) setTimeout(fetchRandomMovie, 100); },
|
||||||
|
'onError': () => {
|
||||||
|
console.error(`YouTube prevented playback of '${movie.Name}'`);
|
||||||
|
if (player) { player.destroy(); player = null; }
|
||||||
|
['backdrop', 'plot', 'lorem-ipsum'].forEach(cls => {
|
||||||
|
const element = document.querySelector(`.${cls}`);
|
||||||
|
if (element) element.style.width = '100%';
|
||||||
|
});
|
||||||
|
videoContainer.style.width = '0';
|
||||||
|
startSlideChangeTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else startSlideChangeTimer();
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.appendChild(slide);
|
||||||
|
};
|
||||||
|
|
||||||
|
function addSwipeListeners(slide) {
|
||||||
|
let startX, startY, distX, distY;
|
||||||
|
const threshold = 50;
|
||||||
|
const restraint = 100;
|
||||||
|
|
||||||
|
slide.addEventListener('touchstart', e => {
|
||||||
|
const touch = e.touches[0];
|
||||||
|
startX = touch.clientX;
|
||||||
|
startY = touch.clientY;
|
||||||
|
});
|
||||||
|
|
||||||
|
slide.addEventListener('touchmove', e => {
|
||||||
|
const touch = e.touches[0];
|
||||||
|
distX = touch.clientX - startX;
|
||||||
|
distY = touch.clientY - startY;
|
||||||
|
});
|
||||||
|
|
||||||
|
slide.addEventListener('touchend', () => {
|
||||||
|
if (Math.abs(distX) > threshold && Math.abs(distY) < restraint) {
|
||||||
|
if (distX > 0) {
|
||||||
|
console.log('Swipe Right');
|
||||||
|
} else {
|
||||||
|
console.log('Swipe Left');
|
||||||
|
fetchRandomMovie();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
distX = distY = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the video overlay
|
||||||
|
function showVideoOverlay(trailerUrl) {
|
||||||
|
const videoOverlay = document.getElementById('video-overlay');
|
||||||
|
const videoFrame = document.getElementById('trailer-video');
|
||||||
|
const closeOverlay = document.getElementById('close-overlay');
|
||||||
|
|
||||||
|
// Extract video ID from trailer URL
|
||||||
|
const videoId = new URL(trailerUrl).searchParams.get('v');
|
||||||
|
const embedUrl = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
|
||||||
|
|
||||||
|
// Set iframe's source to trailer URL
|
||||||
|
videoFrame.src = embedUrl;
|
||||||
|
|
||||||
|
// Show the overlay
|
||||||
|
videoOverlay.style.display = 'block';
|
||||||
|
|
||||||
|
// Pause the slide timer when the video overlay is open
|
||||||
|
clearSlideChangeTimeout();
|
||||||
|
|
||||||
|
// Close the overlay when the X is clicked
|
||||||
|
closeOverlay.onclick = () => {
|
||||||
|
videoOverlay.style.display = 'none';
|
||||||
|
videoFrame.src = ''; // Stop the video
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close the overlay when clicking outside content
|
||||||
|
window.onclick = (event) => {
|
||||||
|
if (event.target === videoOverlay) {
|
||||||
|
videoOverlay.style.display = 'none';
|
||||||
|
videoFrame.src = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close video overlay and restart slide timer
|
||||||
|
function closeVideoOverlay() {
|
||||||
|
const videoOverlay = document.getElementById('video-overlay');
|
||||||
|
const videoFrame = document.getElementById('trailer-video');
|
||||||
|
|
||||||
|
// Hide overlay
|
||||||
|
videoOverlay.style.display = 'none';
|
||||||
|
|
||||||
|
// Reset the iframe source
|
||||||
|
videoFrame.src = '';
|
||||||
|
|
||||||
|
// Restart slide change timer when video overlay is closed
|
||||||
|
startSlideChangeTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSlideChangeTimeout() {
|
||||||
|
if (slideChangeTimeout) {
|
||||||
|
clearTimeout(slideChangeTimeout);
|
||||||
|
slideChangeTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startSlideChangeTimer = () => { clearTimeout(slideChangeTimeout); slideChangeTimeout = setTimeout(fetchRandomMovie, shuffleInterval); };
|
||||||
|
|
||||||
|
const checkBackdropAndLogo = movie => {
|
||||||
|
Promise.all(['/Images/Backdrop/0', '/Images/Logo'].map(url =>
|
||||||
|
fetch(`/Items/${movie.Id}${url}`, { method: 'HEAD' }).then(response => response.ok)
|
||||||
|
)).then(([backdropExists, logoExists]) =>
|
||||||
|
backdropExists && logoExists ? createSlideElement(movie, true) : fetchRandomMovie()
|
||||||
|
).catch(() => fetchRandomMovie());
|
||||||
|
};
|
||||||
|
|
||||||
|
const readCustomList = () =>
|
||||||
|
fetch('list.txt?' + new Date().getTime())
|
||||||
|
.then(response => response.ok ? response.text() : null)
|
||||||
|
.then(text => {
|
||||||
|
if (!text) return null;
|
||||||
|
const lines = text.split('\n').filter(Boolean);
|
||||||
|
title = lines.shift() || title;
|
||||||
|
return lines.map(line => line.trim().substring(0, 32));
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
|
const fetchRandomMovie = () => {
|
||||||
|
if (isChangingSlide) return;
|
||||||
|
isChangingSlide = true;
|
||||||
|
if (movieList.length === 0) {
|
||||||
|
readCustomList().then(list => {
|
||||||
|
if (list) { movieList = list; currentMovieIndex = 0; }
|
||||||
|
fetchNextMovie();
|
||||||
|
});
|
||||||
|
} else fetchNextMovie();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchNextMovie = () => {
|
||||||
|
const fetchCurrentUserId = () =>
|
||||||
|
fetch('/Sessions', {
|
||||||
|
headers: { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` }
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(sessions => {
|
||||||
|
const currentSession = sessions.find(session => session.UserId);
|
||||||
|
return currentSession ? currentSession.UserId : null;
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
|
fetchCurrentUserId().then(currentUserId => {
|
||||||
|
if (!currentUserId) {
|
||||||
|
console.error('Could not retrieve the current user ID.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` };
|
||||||
|
|
||||||
|
if (movieList.length > 0) {
|
||||||
|
if (currentMovieIndex >= movieList.length) currentMovieIndex = 0;
|
||||||
|
const movieId = movieList[currentMovieIndex];
|
||||||
|
currentMovieIndex++;
|
||||||
|
|
||||||
|
fetch(`/Users/${currentUserId}/Items/${movieId}?Fields=Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(checkBackdropAndLogo)
|
||||||
|
.catch(() => startSlideChangeTimer())
|
||||||
|
.finally(() => { isChangingSlide = false; });
|
||||||
|
} else {
|
||||||
|
const itemTypes = moviesSeriesBoth === 1 ? 'Movie' : (moviesSeriesBoth === 2 ? 'Series' : 'Movie,Series');
|
||||||
|
fetch(`/Users/${currentUserId}/Items?IncludeItemTypes=${itemTypes}&Recursive=true&Limit=1&SortBy=random&Fields=Id,Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => { if (data.Items[0]) checkBackdropAndLogo(data.Items[0]); })
|
||||||
|
.catch(() => startSlideChangeTimer())
|
||||||
|
.finally(() => { isChangingSlide = false; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkNavigation = () => {
|
||||||
|
const newLocation = window.top.location.href;
|
||||||
|
if (newLocation !== currentLocation) {
|
||||||
|
currentLocation = newLocation;
|
||||||
|
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
||||||
|
if (isHomePage(newLocation)) {
|
||||||
|
if (!isHomePageActive) {
|
||||||
|
console.log("Returning to homepage, reactivating slideshow");
|
||||||
|
isHomePageActive = true;
|
||||||
|
cleanup();
|
||||||
|
fetchRandomMovie();
|
||||||
|
}
|
||||||
|
} else if (isHomePageActive) {
|
||||||
|
console.log("Leaving homepage, cleaning up slideshow");
|
||||||
|
isHomePageActive = false;
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(checkNavigation, 100);
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
if (window.innerWidth < 1001) useTrailers = false;
|
||||||
|
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
||||||
|
if (isHomePage(window.top.location.href)) {
|
||||||
|
isHomePageActive = true;
|
||||||
|
readCustomList().then(list => {
|
||||||
|
if (list) { movieList = list; currentMovieIndex = 0; }
|
||||||
|
fetchRandomMovie();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('unload', cleanup);
|
||||||
|
window.addEventListener('popstate', checkNavigation);
|
||||||
|
</script>
|
||||||
|
<script src="https://www.youtube.com/iframe_api"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em;}} </style> <iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
<style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em; height: 25em;}} </style> <iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -25,7 +25,40 @@
|
|||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
.featurediframe {
|
.featurediframe {
|
||||||
margin-top: 0.8em;
|
margin-top: 0.8em;
|
||||||
|
height: 25em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
<iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"use strict"; (self.webpackChunk = self.webpackChunk || []).push([[8372], { 5939: function (a, e, t) { t.r(e), e.default = '<div id="indexPage" style="outline:0" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie,series,book"><style> .featurediframe {width: 95vw; height: 24em; display: block; border: 0; margin: -1em auto 0;} @media (min-width: 2100px) {.featurediframe {height: 33em;}} @media (max-width: 1599px) {.featurediframe {margin-top: 1.2em;}} @media (max-width: 800px) {.featurediframe {margin-top: 0.8em; height: 25em;}} </style> <iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe> <style>:root { --save-gut: max(env(safe-area-inset-left), .3%) } .requestIframe { margin: 0 .4em; padding: 0 var(--save-gut); width: calc(100% - (.4em * 2) - (var(--save-gut) * 2)); height: 90vh; border: none; position: absolute; top: 5.3em } @media (max-width: 1599px) { .requestIframe { height: 83vh; top: 8.2em; } }</style><script>setTimeout(() => { createRequestTab() }, 500)</script> <div class="tabContent pageTabContent" id="homeTab" data-index="0"> <div class="sections"></div> </div> <div class="tabContent pageTabContent" id="favoritesTab" data-index="1"> <div class="sections"></div> </div><div class="tabContent pageTabContent" id="requestsTab" data-index="2"> <div class="sections"><iframe class="requestIframe" src="https://jellyseerr.mahom03-spacecloud.de"></iframe></div> </div> </div> ' } }]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NEU (aber nicht genutzt):
|
||||||
|
<style> .featurediframe {width: 95vw; height: 23.5em; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em;} @media (min-width: 3158px) {.featurediframe {height: 50em;} } @media (min-width: 2601px) and (max-width: 3157px) {.featurediframe {height: 33em;} } @media (min-width: 2000px) and (max-width: 2600px) {.featurediframe {height: 27em; font-size: 133%;} .layout-desktop #homeTab .sections.homeSectionsContainer {margin-top: -3em !important;} } @media (max-width: 1000px) and (orientation: portrait) {.featurediframe {height: 25em; margin-bottom: -3em;} } @media (max-width: 1000px) and (orientation: landscape) {.featurediframe {height: 26em; margin-bottom: -7em;} } @media (max-width: 400px) and (orientation: portrait) {.featurediframe {height: 45vh; margin-bottom: 0em;} } @media (max-height: 400px) and (orientation: landscape) {.featurediframe {height: 100vh;} } @media screen and (aspect-ratio: 4/3) {.featurediframe {height: 25em;} } @media screen and (aspect-ratio: 3/4) {.featurediframe {height: 25em; margin-bottom: -5em;} } @media screen and (aspect-ratio: 16/10) and (max-height: 1200px) {.featurediframe {height: 34em; margin-bottom: -5em;} } @media screen and (aspect-ratio: 10/16) and (max-height: 1280px) {.featurediframe {height: 25em; margin-bottom: -5em;} } @media (min-aspect-ratio: 21/9) and (min-width: 3000px) {.featurediframe { height: 50em;} } </style> <iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.featurediframe {width: 95vw; height: 23.5em; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em;}
|
||||||
|
@media (min-width: 3158px) {.featurediframe {height: 50em;} }
|
||||||
|
@media (min-width: 2601px) and (max-width: 3157px) {.featurediframe {height: 33em;} }
|
||||||
|
@media (min-width: 2000px) and (max-width: 2600px) {.featurediframe {height: 27em; font-size: 133%;} .layout-desktop #homeTab .sections.homeSectionsContainer {margin-top: -3em !important;} }
|
||||||
|
@media (max-width: 1000px) and (orientation: portrait) {.featurediframe {height: 25em; margin-bottom: -3em;} }
|
||||||
|
@media (max-width: 1000px) and (orientation: landscape) {.featurediframe {height: 26em; margin-bottom: -7em;} }
|
||||||
|
@media (max-width: 500px) and (orientation: portrait) {.featurediframe {height: 45vh; margin-bottom: 0em;} }
|
||||||
|
@media (max-height: 500px) and (orientation: landscape) {.featurediframe {height: 100vh;} }
|
||||||
|
@media screen and (aspect-ratio: 4/3) {.featurediframe {height: 25em;} }
|
||||||
|
@media screen and (aspect-ratio: 3/4) {.featurediframe {height: 25em; margin-bottom: -5em;} }
|
||||||
|
@media screen and (aspect-ratio: 16/10) and (max-height: 1200px) {.featurediframe {height: 34em; margin-bottom: -5em;} }
|
||||||
|
@media screen and (aspect-ratio: 10/16) and (max-height: 1280px) {.featurediframe {height: 25em; margin-bottom: -5em;} }
|
||||||
|
@media (min-aspect-ratio: 21/9) and (min-width: 3000px) {.featurediframe { height: 50em;} }
|
||||||
|
</style>
|
||||||
|
<iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"use strict"; (self.webpackChunk = self.webpackChunk || []).push([[8372], { 5939: function (a, e, t) { t.r(e), e.default = '<div id="indexPage" style="outline:0" data-role="page" data-dom-cache="true" class="page homePage libraryPage allLibraryPage backdropPage pageWithAbsoluteTabs withTabs" data-backdroptype="movie,series,book"><style> .featurediframe {width: 95vw; height: 23.5em; display: block; border: 0px solid #000; margin: 0 auto; margin-bottom: 0em; margin-top: 1em;} @media (min-width: 3158px) {.featurediframe {height: 50em;}} @media (min-width: 2601px) and (max-width: 3157px) {.featurediframe {height: 33em;}} @media (min-width: 2000px) and (max-width: 2600px) {.featurediframe {height: 27em; font-size: 133%;} .layout-desktop #homeTab .sections.homeSectionsContainer {margin-top: -3em !important;}} @media (max-width: 1000px) and (orientation: portrait) {.featurediframe {height: 25em; margin-bottom: -3em;}} @media (max-width: 1000px) and (orientation: landscape) {.featurediframe {height: 26em; margin-bottom: -7em;}} @media (max-width: 400px) and (orientation: portrait) {.featurediframe {height: 45vh; margin-bottom: 0em;}} @media (max-height: 400px) and (orientation: landscape) {.featurediframe {height: 100vh;}} @media screen and (aspect-ratio: 4/3) {.featurediframe {height: 25em;}} @media screen and (aspect-ratio: 3/4) {.featurediframe {height: 25em; margin-bottom: -5em;}} @media screen and (aspect-ratio: 16/10) and (max-height: 1200px) {.featurediframe {height: 34em; margin-bottom: -5em;}} @media screen and (aspect-ratio: 10/16) and (max-height: 1280px) {.featurediframe {height: 25em; margin-bottom: -5em;}} @media (min-aspect-ratio: 21/9) and (min-width: 3000px) {.featurediframe { height: 50em;}}</style><iframe class="featurediframe" src="/web/ui/spotlight.html"></iframe> <style>:root { --save-gut: max(env(safe-area-inset-left), .3%) } .requestIframe { margin: 0 .4em; padding: 0 var(--save-gut); width: calc(100% - (.4em * 2) - (var(--save-gut) * 2)); height: 90vh; border: none; position: absolute; top: 5.3em } @media (max-width: 1599px) { .requestIframe { height: 83vh; top: 8.2em; } }</style><script>setTimeout(() => { createRequestTab() }, 500)</script> <div class="tabContent pageTabContent" id="homeTab" data-index="0"> <div class="sections"></div> </div> <div class="tabContent pageTabContent" id="favoritesTab" data-index="1"> <div class="sections"></div> </div><div class="tabContent pageTabContent" id="requestsTab" data-index="2"> <div class="sections"><iframe class="requestIframe" src="https://jellyseerr.mahom03-spacecloud.de"></iframe></div> </div> </div> ' } }]);
|
||||||
|
|||||||
BIN
images/Screenshot 2024-11-14 230158.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
images/Screenshot 2024-11-14 230340.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/Screenshot_20241114_175848_Jellyfin.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
images/Screenshot_20241114_175951_Jellyfin.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
images/all_clips.gif
Normal file
|
After Width: | Height: | Size: 18 MiB |
BIN
images/demo1.gif
Normal file
|
After Width: | Height: | Size: 18 MiB |
BIN
images/desktop.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
images/fullscreen.gif
Normal file
|
After Width: | Height: | Size: 9.9 MiB |
BIN
images/mobile.png
Normal file
|
After Width: | Height: | Size: 5.5 MiB |
@@ -1 +0,0 @@
|
|||||||
wsfaf
|
|
||||||
9
list.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Title of List [muteon/muteoff]
|
||||||
|
f81c9b854e3edc62fb049e252c488115 Hunger Games
|
||||||
|
7d515072462d6cedd74de8e2df71888f Interstellar
|
||||||
|
df4f3da3066455404e6b874d22ba86aa Fast X
|
||||||
|
0eab5162a26ed05546215e762dde8544 Greatest Showman
|
||||||
|
820d0a5c3eaee93e8d3bfbb670a9a41e Mission Impossible 7
|
||||||
|
b948bde88c4e1f48e84f26fec46df211 Oppenheimer
|
||||||
|
daeab01d279b7df4f6726892a09d3846 Im Westen nichts neues
|
||||||
|
5c70c7506a20814c6cfa15d139b4c0b5 Lupin
|
||||||
738
script.js
@@ -1,17 +1,249 @@
|
|||||||
let title = 'Spotlight'; // Title of the slideshow
|
let title = 'Spotlight'; // Title of the slideshow TBD
|
||||||
let listFileName = 'list.txt'; // Name of the file containing the list of movie IDs
|
let listFileName = 'list.txt'; // Name of the file containing the list of movie IDs
|
||||||
let token = 'YOURAPIKEYHERE'; // Your Jellyfin API key
|
|
||||||
let moviesSeriesBoth = 3; // 1 for movies, 2 for series, 3 for both
|
let moviesSeriesBoth = 3; // 1 for movies, 2 for series, 3 for both
|
||||||
let shuffleInterval = 15000; // Time in milliseconds before the next slide is shown, unless trailer is playing
|
let shuffleInterval = 15000; // Time in milliseconds before the next slide is shown, unless trailer is playing
|
||||||
let useTrailers = true; // Set to false to disable trailers
|
let useTrailers = true; // Set to false to disable trailers
|
||||||
let setRandomMovie = true; // Set to false to disable random movie selection from the list
|
let setRandomMovie = true; // Set to false to disable random movie selection from the list
|
||||||
let showOnOtherPages = false; // Set to true to show the slideshow on all pages eg. favorites tab, requests tab, etc.
|
let showOnOtherPages = false; // Set to true to show the slideshow on all pages eg. favorites tab, requests tab, etc.
|
||||||
let showTitle = false; // Set to false to hide the title
|
let showTitle = false; // Set to true to place the slideshow title above the banner
|
||||||
|
let disableTrailerControls = false; // Set to false to enable trailer controls
|
||||||
|
let setMutedHover = true; // Set to false to disable unmuting the video on hover
|
||||||
|
let unmutedVolume = 20; // Set the volume level when the video is unmuted
|
||||||
|
let useSponsorBlock = true; // Set to true to use SponsorBlock data to skip intro/outro segments of trailers
|
||||||
|
let skipIntro = true; // Set to true to skip the intro segment of the trailer
|
||||||
let plotMaxLength = 550; // Maximum number of characters in the plot
|
let plotMaxLength = 550; // Maximum number of characters in the plot
|
||||||
|
let trailerMaxLength = 0; // Default value 0; length measured in ms, set to 0 to disable, could be used instead of SponsorBlock
|
||||||
|
let startTrailerMuted = true; // Default value true; set to false to start the video unmuted
|
||||||
|
|
||||||
|
// Seasonal lists configuration
|
||||||
|
let useSeasonalLists = false; // Set to true to enable automatic seasonal list switching
|
||||||
|
const seasonalLists = {
|
||||||
|
spring: 'spring_list.txt', // spring (march-may)
|
||||||
|
summer: 'summer_list.txt', // summer (june-august)
|
||||||
|
autumn: 'autumn_list.txt', // autumn (september-november)
|
||||||
|
winter: 'winter_list.txt', // winter (december-february)
|
||||||
|
newyear: 'newyear_list.txt', // new year (1.-7. januar)
|
||||||
|
valentine: 'valentine_list.txt', // valentines day (10.-20. februar)
|
||||||
|
easter: 'easter_list.txt', // easter (variable dates, March-April)
|
||||||
|
halloween: 'halloween_list.txt' // halloween (20.-31. october)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Language specific strings
|
||||||
|
// currently implemented: en, de, fr, es, it, pl, nl
|
||||||
|
const seasonTerms = ["Season", "Staffel", "Saison", "Temporada", "Stagione", "Sezon", "Seizoen"];
|
||||||
|
const seasonsTerms = ["Seasons", "Staffeln", "Saisons", "Temporadas", "Stagioni", "Sezony", "Seizoenen"];
|
||||||
|
const unknownSeasonsTerms = ["Unknown seasons", "Unbekannte Staffeln","Saisons inconnues", "Temporadas desconocidas", "Stagioni sconosciute", "Nieznane sezony", "Onbekende seizoenen"];
|
||||||
|
const movieTerms = ["Movie", "Film", "Film", "Película", "Film", "Film", "Film"];
|
||||||
|
const moviesTerms = ["Movies", "Filme", "Films", "Películas", "Film", "Filmy", "Films"];
|
||||||
|
const unknownMoviesTerms = ["Unknown movies", "Unbekannte Filme", "Films inconnus", "Películas desconocidas", "Film sconosciuti", "Nieznane filmy", "Onbekende films"];
|
||||||
|
const unknownYearTerms = ["Unknown year", "Unbekanntes Jahr", "Année inconnue", "Año desconocido", "Anno sconosciuto", "Nieznany rok", "Onbekend jaar"];
|
||||||
|
const unknownRuntimeTerms = ["Unknown Runtime", "Unbekannte Länge", "Durée inconnue", "Duración desconocida", "Durata sconosciuta", "Nieznany czas trwania", "Onbekende duur"];
|
||||||
|
|
||||||
|
// get the Jellyfin credentials from the local storage (api token and user id)
|
||||||
|
const getJellyfinCredentials = () => {
|
||||||
|
const jellyfinCreds = localStorage.getItem("jellyfin_credentials");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const serverCredentials = JSON.parse(jellyfinCreds);
|
||||||
|
|
||||||
|
const firstServer = serverCredentials.Servers[0];
|
||||||
|
|
||||||
|
if (!firstServer) {
|
||||||
|
console.error("Could not find credentials for the client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { token: firstServer.AccessToken, userId: firstServer.UserId };
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Could not parse jellyfin credentials", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { token, userId } = getJellyfinCredentials();
|
||||||
|
|
||||||
|
|
||||||
|
// variables
|
||||||
let isChangingSlide = false, player = null, slideChangeTimeout = null, isHomePageActive = false;
|
let isChangingSlide = false, player = null, slideChangeTimeout = null, isHomePageActive = false;
|
||||||
let currentLocation = window.top.location.href;
|
let currentLocation = window.top.location.href;
|
||||||
let movieList = [], currentMovieIndex = 0;
|
let movieList = [], currentMovieIndex = 0;
|
||||||
|
let previousMovies = [];
|
||||||
|
let forwardMovies = [];
|
||||||
|
let monitorOutroInterval = null; // Global interval variable to monitor the outro segment of the trailer
|
||||||
|
let isMuted = startTrailerMuted; userInteracted = false;
|
||||||
|
const baseBackdropOverlapVW = 11.4;
|
||||||
|
let activeTrailerLayout = null;
|
||||||
|
|
||||||
|
|
||||||
|
// Get the current browser language
|
||||||
|
const getBrowserLanguage = () => {
|
||||||
|
const language = navigator.language || navigator.userLanguage;
|
||||||
|
return language.split('-')[0]; // Return the language code (e.g., 'en', 'de')
|
||||||
|
};
|
||||||
|
const browserLanguage = getBrowserLanguage();
|
||||||
|
|
||||||
|
// set corresponding language index
|
||||||
|
const setLanguage = (language) => {
|
||||||
|
switch (language) {
|
||||||
|
case 'de':
|
||||||
|
return 1;
|
||||||
|
case 'fr':
|
||||||
|
return 2;
|
||||||
|
case 'es':
|
||||||
|
return 3;
|
||||||
|
case 'it':
|
||||||
|
return 4;
|
||||||
|
case 'pl':
|
||||||
|
return 5;
|
||||||
|
case 'nl':
|
||||||
|
return 6;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const languageIndex = setLanguage(browserLanguage);
|
||||||
|
|
||||||
|
// Seasonal list detection
|
||||||
|
const getCurrentSeason = () => {
|
||||||
|
if (!useSeasonalLists) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const month = now.getMonth() + 1; // 1-12
|
||||||
|
const day = now.getDate();
|
||||||
|
|
||||||
|
// Special events (take precedence over seasons)
|
||||||
|
// new year: 1-7 january
|
||||||
|
if (month === 1 && day <= 7) return 'newyear';
|
||||||
|
|
||||||
|
// valentines day: 10-20 february
|
||||||
|
if (month === 2 && day >= 10 && day <= 20) return 'valentine';
|
||||||
|
|
||||||
|
// halloween: 20-31 cotober
|
||||||
|
if (month === 10 && day >= 20) return 'halloween';
|
||||||
|
|
||||||
|
// Easter calculation (simplified - around March-April)
|
||||||
|
const easterStart = getEasterPeriod(now.getFullYear());
|
||||||
|
if (isInEasterPeriod(now, easterStart)) return 'easter';
|
||||||
|
|
||||||
|
// Regular seasons
|
||||||
|
if (month >= 3 && month <= 5) return 'spring'; // march-may
|
||||||
|
if (month >= 6 && month <= 8) return 'summer'; // june-august
|
||||||
|
if (month >= 9 && month <= 11) return 'autumn'; // september-november
|
||||||
|
if (month === 12 || month <= 2) return 'winter'; // december-february
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simplified Easter calculation (Western Easter)
|
||||||
|
const getEasterPeriod = (year) => {
|
||||||
|
const a = year % 19;
|
||||||
|
const b = Math.floor(year / 100);
|
||||||
|
const c = year % 100;
|
||||||
|
const d = Math.floor(b / 4);
|
||||||
|
const e = b % 4;
|
||||||
|
const f = Math.floor((b + 8) / 25);
|
||||||
|
const g = Math.floor((b - f + 1) / 3);
|
||||||
|
const h = (19 * a + b - d - g + 15) % 30;
|
||||||
|
const i = Math.floor(c / 4);
|
||||||
|
const k = c % 4;
|
||||||
|
const l = (32 + 2 * e + 2 * i - h - k) % 7;
|
||||||
|
const m = Math.floor((a + 11 * h + 22 * l) / 451);
|
||||||
|
const month = Math.floor((h + l - 7 * m + 114) / 31);
|
||||||
|
const day = ((h + l - 7 * m + 114) % 31) + 1;
|
||||||
|
|
||||||
|
return { month, day };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if current date is in Easter period (2 weeks around Easter)
|
||||||
|
const isInEasterPeriod = (currentDate, easter) => {
|
||||||
|
const easterDate = new Date(currentDate.getFullYear(), easter.month - 1, easter.day);
|
||||||
|
const timeDiff = Math.abs(currentDate.getTime() - easterDate.getTime());
|
||||||
|
const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||||
|
return daysDiff <= 7; // 1 week before and after Easter
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the appropriate list filename based on season
|
||||||
|
const getSeasonalListFileName = async () => {
|
||||||
|
const season = getCurrentSeason();
|
||||||
|
if (!season || !seasonalLists[season]) {
|
||||||
|
console.log('Using default list:', listFileName);
|
||||||
|
return listFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if seasonal file exists
|
||||||
|
const seasonalFile = seasonalLists[season];
|
||||||
|
try {
|
||||||
|
const response = await fetch(seasonalFile + '?' + new Date().getTime(), { method: 'HEAD' });
|
||||||
|
if (response.ok) {
|
||||||
|
console.log(`Using seasonal list for ${season}:`, seasonalFile);
|
||||||
|
return seasonalFile;
|
||||||
|
} else {
|
||||||
|
console.warn(`Seasonal file ${seasonalFile} not found, falling back to default list:`, listFileName);
|
||||||
|
return listFileName;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error checking seasonal file ${seasonalFile}:`, error.message, '- falling back to default list:', listFileName);
|
||||||
|
return listFileName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get SponsorBlock-Data for the outro/intro segment of the trailer
|
||||||
|
const fetchSponsorBlockData = async (videoId) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${videoId}&categories=["intro","outro"]`);
|
||||||
|
const segments = await response.json();
|
||||||
|
|
||||||
|
let intro = null;
|
||||||
|
let outro = null;
|
||||||
|
|
||||||
|
segments.forEach(segment => {
|
||||||
|
if (segment.category === "intro" && Array.isArray(segment.segment)) {
|
||||||
|
intro = segment.segment; // [start, end] of the intro
|
||||||
|
} else if (segment.category === "outro" && Array.isArray(segment.segment)) {
|
||||||
|
outro = segment.segment; // [start, end] of the outro
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { intro, outro };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching SponsorBlock data:', error);
|
||||||
|
return { intro: null, outro: null };
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Monitor the video player for the outro segment
|
||||||
|
function monitorOutro(player, outroSegment) {
|
||||||
|
if (monitorOutroInterval) { // Clear the interval if it's already running
|
||||||
|
clearInterval(monitorOutroInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorOutroInterval = setInterval(() => {
|
||||||
|
if (!outroSegment || !player) {
|
||||||
|
console.log('Invalid outro segment or player not initialized');
|
||||||
|
clearInterval(monitorOutroInterval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = player.getCurrentTime();
|
||||||
|
if (currentTime >= outroSegment[0] && currentTime < outroSegment[1]) {
|
||||||
|
clearInterval(monitorOutroInterval);
|
||||||
|
player.stopVideo(); // stop video
|
||||||
|
setTimeout(fetchRandomMovie, 100); // fetch next movie
|
||||||
|
}
|
||||||
|
}, 500); // check every 500ms
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearMonitorOutroInterval = () => {
|
||||||
|
if (monitorOutroInterval) {
|
||||||
|
clearInterval(monitorOutroInterval);
|
||||||
|
monitorOutroInterval = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const createElem = (tag, className, textContent, src, alt) => {
|
const createElem = (tag, className, textContent, src, alt) => {
|
||||||
const elem = document.createElement(tag);
|
const elem = document.createElement(tag);
|
||||||
@@ -30,24 +262,175 @@ function isMobile() {
|
|||||||
const truncateText = (text, maxLength) => text.length > maxLength ? text.substr(0, maxLength) + '...' : text;
|
const truncateText = (text, maxLength) => text.length > maxLength ? text.substr(0, maxLength) + '...' : text;
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
if (player) { player.destroy(); player = null; }
|
clearTrailerLayout();
|
||||||
|
if (player && typeof player.destroy === 'function') {
|
||||||
|
player.destroy();
|
||||||
|
}
|
||||||
|
player = null;
|
||||||
clearTimeout(slideChangeTimeout);
|
clearTimeout(slideChangeTimeout);
|
||||||
|
slideChangeTimeout = null;
|
||||||
|
clearMonitorOutroInterval();
|
||||||
const container = document.getElementById('slides-container');
|
const container = document.getElementById('slides-container');
|
||||||
if (container) container.innerHTML = '';
|
if (container) container.innerHTML = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearTrailerLayout = () => {
|
||||||
|
if (!activeTrailerLayout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { videoContainer, backdrop, plot, genres, loremIpsum, logo } = activeTrailerLayout;
|
||||||
|
|
||||||
|
if (videoContainer) {
|
||||||
|
videoContainer.style.removeProperty('width');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backdrop) {
|
||||||
|
backdrop.style.removeProperty('width');
|
||||||
|
backdrop.style.removeProperty('left');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plot) {
|
||||||
|
plot.style.removeProperty('left');
|
||||||
|
plot.style.removeProperty('max-width');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genres) {
|
||||||
|
genres.style.removeProperty('left');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loremIpsum) {
|
||||||
|
loremIpsum.style.removeProperty('left');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logo) {
|
||||||
|
logo.style.removeProperty('left');
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTrailerLayout = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyTrailerLayout = (videoContainer) => {
|
||||||
|
if (!videoContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTrailerLayout && activeTrailerLayout.videoContainer !== videoContainer) {
|
||||||
|
clearTrailerLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const slideWrapper = videoContainer.closest('.slide-wrapper');
|
||||||
|
if (!slideWrapper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slideElement = slideWrapper.querySelector('.slide');
|
||||||
|
const containerRect = videoContainer.getBoundingClientRect();
|
||||||
|
const slideRect = slideElement ? slideElement.getBoundingClientRect() : null;
|
||||||
|
const effectiveHeight = containerRect.height || (slideRect ? slideRect.height : 0);
|
||||||
|
const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 1920;
|
||||||
|
|
||||||
|
if (!effectiveHeight || !viewportWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idealWidthVw = (effectiveHeight * (16 / 9)) / viewportWidth * 100;
|
||||||
|
const videoWidthVw = Math.max(idealWidthVw, 18);
|
||||||
|
const videoWidthVwCapped = Math.min(videoWidthVw, 34.4);
|
||||||
|
const videoWidthVwRounded = Math.min(Math.max(videoWidthVwCapped, 10), 60);
|
||||||
|
|
||||||
|
const backdrop = slideWrapper.querySelector('.backdrop');
|
||||||
|
const plot = slideWrapper.querySelector('.plot');
|
||||||
|
const genres = slideWrapper.querySelector('.genres');
|
||||||
|
const loremIpsum = slideWrapper.querySelector('.lorem-ipsum');
|
||||||
|
const logo = slideWrapper.querySelector('.logo');
|
||||||
|
|
||||||
|
videoContainer.style.width = `${videoWidthVwRounded.toFixed(3)}vw`;
|
||||||
|
|
||||||
|
if (backdrop) {
|
||||||
|
const backdropGap = Math.max(videoWidthVwRounded - baseBackdropOverlapVW, 0).toFixed(3);
|
||||||
|
backdrop.style.width = `calc(100% - ${backdropGap}vw)`;
|
||||||
|
backdrop.style.left = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plot) {
|
||||||
|
const availableAreaVw = Math.max(100 - videoWidthVwRounded, 20);
|
||||||
|
const maxWidth = Math.max(availableAreaVw - 4, 30).toFixed(3);
|
||||||
|
plot.style.left = `calc(50% - ${(videoWidthVwRounded / 2).toFixed(3)}vw)`;
|
||||||
|
plot.style.maxWidth = `${maxWidth}vw`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const anchorShift = `calc(50% - ${(videoWidthVwRounded / 2).toFixed(3)}vw)`;
|
||||||
|
if (genres) {
|
||||||
|
genres.style.left = anchorShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loremIpsum) {
|
||||||
|
loremIpsum.style.left = anchorShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logo) {
|
||||||
|
logo.style.left = anchorShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTrailerLayout = {
|
||||||
|
videoContainer,
|
||||||
|
backdrop,
|
||||||
|
plot,
|
||||||
|
genres,
|
||||||
|
loremIpsum,
|
||||||
|
logo
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (activeTrailerLayout && document.contains(activeTrailerLayout.videoContainer)) {
|
||||||
|
applyTrailerLayout(activeTrailerLayout.videoContainer);
|
||||||
|
} else {
|
||||||
|
activeTrailerLayout = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const createSlideElement = (movie, hasVideo = false) => {
|
const createSlideElement = (movie, hasVideo = false) => {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
isMuted = startTrailerMuted;
|
||||||
const container = document.getElementById('slides-container');
|
const container = document.getElementById('slides-container');
|
||||||
|
const slideWrapper = createElem('div', 'slide-wrapper');
|
||||||
const slide = createElem('div', 'slide');
|
const slide = createElem('div', 'slide');
|
||||||
|
|
||||||
|
if (movie && (!previousMovies.length || previousMovies[previousMovies.length - 1].Id !== movie.Id)) {
|
||||||
|
previousMovies.push(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousMovies.length > 50) {
|
||||||
|
previousMovies.shift();
|
||||||
|
}
|
||||||
['backdrop', 'logo'].forEach(type => slide.appendChild(createElem('img', type, null, `/Items/${movie.Id}/Images/${type.charAt(0).toUpperCase() + type.slice(1)}${type === 'backdrop' ? '/0' : ''}`, type)));
|
['backdrop', 'logo'].forEach(type => slide.appendChild(createElem('img', type, null, `/Items/${movie.Id}/Images/${type.charAt(0).toUpperCase() + type.slice(1)}${type === 'backdrop' ? '/0' : ''}`, type)));
|
||||||
slide.appendChild(createElem('div', 'heading', title));
|
|
||||||
|
if (showTitle && title) {
|
||||||
|
const heading = createElem('div', 'heading', title);
|
||||||
|
heading.style.display = 'flex';
|
||||||
|
slideWrapper.classList.add('has-heading');
|
||||||
|
slideWrapper.appendChild(heading);
|
||||||
|
}
|
||||||
|
|
||||||
const textContainer = createElem('div', 'text-container');
|
const textContainer = createElem('div', 'text-container');
|
||||||
const premiereYear = movie.PremiereDate ? new Date(movie.PremiereDate).getFullYear() : 'Unknown';
|
const premiereYear = movie.PremiereDate ? new Date(movie.PremiereDate).getFullYear() : unknownYearTerms[languageIndex];
|
||||||
const additionalInfo = movie.Type === 'Series' ?
|
|
||||||
(movie.ChildCount ? `${movie.ChildCount} Season${movie.ChildCount > 1 ? 's' : ''}` : 'Unknown Seasons') :
|
let additionalInfo;
|
||||||
(movie.RunTimeTicks ? `${Math.round(movie.RunTimeTicks / 600000000)} min` : 'Unknown Runtime');
|
if (movie.Type === 'Series') {
|
||||||
|
additionalInfo = movie.ChildCount
|
||||||
|
? `${movie.ChildCount} ${movie.ChildCount > 1 ? seasonsTerms[languageIndex] : seasonTerms[languageIndex]}`
|
||||||
|
: unknownSeasonsTerms[languageIndex];
|
||||||
|
} else if (movie.Type === 'BoxSet') {
|
||||||
|
additionalInfo = movie.ChildCount
|
||||||
|
? `${movie.ChildCount} ${movie.ChildCount > 1 ? moviesTerms[languageIndex] : movieTerms[languageIndex]}`
|
||||||
|
: unknownMoviesTerms[languageIndex];
|
||||||
|
} else {
|
||||||
|
additionalInfo = movie.RunTimeTicks
|
||||||
|
? `${Math.round(movie.RunTimeTicks / 600000000)} min`
|
||||||
|
: unknownRuntimeTerms[languageIndex];
|
||||||
|
}
|
||||||
|
|
||||||
let loremText = `
|
let loremText = `
|
||||||
<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">${additionalInfo}</span>
|
<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">${additionalInfo}</span>
|
||||||
@@ -60,7 +443,7 @@ const createSlideElement = (movie, hasVideo = false) => {
|
|||||||
}
|
}
|
||||||
if (movie.CriticRating) {
|
if (movie.CriticRating) {
|
||||||
loremText += `<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">
|
loremText += `<span style="background: transparent; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.05em; padding-bottom: 0.05em; margin-left: 1em;">
|
||||||
<img src="https://i.imgur.com/rMvyQMt.png" alt="Rotten Tomatoes" style="width: 1.05em; height: 1.25em; font-size: 0.9em; padding-right: 0.1em; margin-left: -0.1em; vertical-align: bottom;">
|
<img src="https://i.imgur.com/aoLXyXx.png" alt="Rotten Tomatoes" style="width: 1.05em; height: 1.25em; font-size: 0.83em; padding-right: 0.4em; margin-left: -0.1em; vertical-align: middle; padding-bottom: 0.3em;">
|
||||||
${movie.CriticRating}%</span>`;
|
${movie.CriticRating}%</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,15 +494,22 @@ const createSlideElement = (movie, hasVideo = false) => {
|
|||||||
const backButton = createElem('div', 'back-button');
|
const backButton = createElem('div', 'back-button');
|
||||||
const backIcon = createElem('i', 'material-icons');
|
const backIcon = createElem('i', 'material-icons');
|
||||||
backIcon.textContent = 'chevron_left';
|
backIcon.textContent = 'chevron_left';
|
||||||
|
backButton.appendChild(backIcon);
|
||||||
|
backButton.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip button logic
|
||||||
const skipButton = createElem('div', 'skip-button');
|
const skipButton = createElem('div', 'skip-button');
|
||||||
const skipIcon = createElem('i', 'material-icons');
|
const skipIcon = createElem('i', 'material-icons');
|
||||||
skipIcon.textContent = 'chevron_right';
|
skipIcon.textContent = 'chevron_right';
|
||||||
|
|
||||||
backButton.appendChild(backIcon);
|
|
||||||
skipButton.appendChild(skipIcon);
|
skipButton.appendChild(skipIcon);
|
||||||
|
skipButton.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
navigateForward();
|
||||||
|
};
|
||||||
|
|
||||||
skipIcon.onclick = (e) => { e.stopPropagation(); fetchRandomMovie(); };
|
|
||||||
backIcon.onclick = (e) => { e.stopPropagation(); previousMovie(); };
|
|
||||||
slide.appendChild(backButton);
|
slide.appendChild(backButton);
|
||||||
slide.appendChild(skipButton);
|
slide.appendChild(skipButton);
|
||||||
|
|
||||||
@@ -177,79 +567,94 @@ const createSlideElement = (movie, hasVideo = false) => {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
videoId,
|
videoId,
|
||||||
events: {
|
playerVars: {
|
||||||
'onReady': event => {
|
mute: isMuted ? 1 : 0, // Mute the video
|
||||||
event.target.playVideo();
|
controls: disableTrailerControls ? 0 : 1, // Hide the controls
|
||||||
|
disablekb: 1, // Disable keyboard controls
|
||||||
|
iv_load_policy: 3, // Disable annotations
|
||||||
|
cc_load_policy: 3, // Disable captions
|
||||||
},
|
},
|
||||||
'onStateChange': event => {
|
events: {
|
||||||
if (event.data === YT.PlayerState.PLAYING) {
|
'onReady': () => {
|
||||||
// Only show when YT video is successfully playing
|
if (useSponsorBlock) {
|
||||||
const backdrop = document.querySelector('.backdrop');
|
fetchSponsorBlockData(videoId).then(({ intro, outro }) => {
|
||||||
if (backdrop) {
|
if (intro && skipIntro) {
|
||||||
backdrop.style.width = 'calc(100% - 23vw)';
|
console.log(`SponsorBlock intro segment: Start - ${intro[0]}s, End - ${intro[1]}s`);
|
||||||
backdrop.style.left = '0vw';
|
if (player && typeof player.seekTo === 'function') {
|
||||||
|
player.seekTo(intro[1], true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('No intro segment found/provided or intro skipping is disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
const plot = document.querySelector('.plot');
|
if (outro) {
|
||||||
if (plot) plot.style.width = 'calc(100% - 36.4vw)';
|
console.log(`SponsorBlock outro segment: Start - ${outro[0]}s, End - ${outro[1]}s`);
|
||||||
|
monitorOutro(player, outro);
|
||||||
|
} else {
|
||||||
|
console.log('No outro segment found/provided');
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error reading SponsorBlock data:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (trailerMaxLength > 0) {
|
||||||
|
clearTimeout(slideChangeTimeout);
|
||||||
|
slideChangeTimeout = setTimeout(() => {
|
||||||
|
clearMonitorOutroInterval();
|
||||||
|
if (player) {
|
||||||
|
player.stopVideo();
|
||||||
|
player.destroy();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
fetchRandomMovie();
|
||||||
|
}, trailerMaxLength);
|
||||||
|
}
|
||||||
|
if (isMuted) {
|
||||||
|
player.mute(); // Mute the video if the setting is MuteOn
|
||||||
|
} else {
|
||||||
|
player.unMute();
|
||||||
|
}
|
||||||
|
|
||||||
const loremIpsum = document.querySelector('.lorem-ipsum');
|
player.playVideo();
|
||||||
if (loremIpsum) loremIpsum.style.paddingRight = '32.4vw';
|
player.setVolume(unmutedVolume); // Set the volume, value between 0 and 100
|
||||||
|
console.log(`Playing trailer for '${movie.Name}'`);
|
||||||
const logo = document.querySelector('.logo');
|
},
|
||||||
if (logo) logo.style.left = 'calc(50% - 14.2vw)';
|
'onStateChange': (event) => {
|
||||||
|
if (event.data === YT.PlayerState.PLAYING) {
|
||||||
videoContainer.style.width = '34.4vw';
|
applyTrailerLayout(videoContainer);
|
||||||
} else if (event.data === YT.PlayerState.ENDED) {
|
} else if (event.data === YT.PlayerState.ENDED) {
|
||||||
setTimeout(fetchRandomMovie, 100);
|
clearTrailerLayout();
|
||||||
|
clearMonitorOutroInterval();
|
||||||
|
setTimeout(fetchRandomMovie, 20);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'onError': () => {
|
'onError': () => {
|
||||||
console.error(`YouTube prevented playback of '${movie.Name}'`);
|
console.error(`YouTube prevented playback of '${movie.Name}'`);
|
||||||
if (player) {
|
if (player) {
|
||||||
|
clearMonitorOutroInterval();
|
||||||
player.destroy();
|
player.destroy();
|
||||||
player = null;
|
player = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset style when a YT error occurs
|
clearTrailerLayout();
|
||||||
const backdrop = document.querySelector('.backdrop');
|
|
||||||
if (backdrop) backdrop.style.width = '100%';
|
|
||||||
|
|
||||||
const plot = document.querySelector('.plot');
|
|
||||||
if (plot) plot.style.width = '98%';
|
|
||||||
|
|
||||||
const loremIpsum = document.querySelector('.lorem-ipsum');
|
|
||||||
if (loremIpsum) loremIpsum.style.paddingRight = '0';
|
|
||||||
|
|
||||||
const logo = document.querySelector('.logo');
|
|
||||||
if (logo) logo.style.left = '50%';
|
|
||||||
|
|
||||||
videoContainer.style.width = '0';
|
videoContainer.style.width = '0';
|
||||||
|
|
||||||
startSlideChangeTimer();
|
startSlideChangeTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
startSlideChangeTimer();
|
startSlideChangeTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
slideWrapper.appendChild(slide);
|
||||||
|
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
container.appendChild(slide);
|
container.appendChild(slideWrapper);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch the previous movie in the list
|
|
||||||
function previousMovie() {
|
|
||||||
if (isChangingSlide) return;
|
|
||||||
isChangingSlide = true;
|
|
||||||
|
|
||||||
// Reset index or set to the end of the list if we are at the first element
|
|
||||||
currentMovieIndex = (currentMovieIndex - 2 + movieList.length) % movieList.length;
|
|
||||||
|
|
||||||
fetchNextMovie();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSwipeListeners(slide) {
|
function addSwipeListeners(slide) {
|
||||||
let startX, startY, distX, distY;
|
let startX, startY, distX, distY;
|
||||||
const threshold = 50;
|
const threshold = 50;
|
||||||
@@ -280,6 +685,33 @@ function addSwipeListeners(slide) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const navigateBack = () => {
|
||||||
|
if (previousMovies.length < 2) {
|
||||||
|
console.log("No previous slides in history.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentMovie = previousMovies.pop();
|
||||||
|
forwardMovies.unshift(currentMovie);
|
||||||
|
|
||||||
|
const previousMovie = previousMovies[previousMovies.length - 1];
|
||||||
|
createSlideElement(previousMovie, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigateForward = () => {
|
||||||
|
if (forwardMovies.length === 0) {
|
||||||
|
console.log("No forward slides in history.");
|
||||||
|
fetchRandomMovie();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentMovie = forwardMovies.shift();
|
||||||
|
previousMovies.push(currentMovie);
|
||||||
|
|
||||||
|
|
||||||
|
createSlideElement(currentMovie, true);
|
||||||
|
};
|
||||||
|
|
||||||
// Show the video overlay
|
// Show the video overlay
|
||||||
function showVideoOverlay(trailerUrl) {
|
function showVideoOverlay(trailerUrl) {
|
||||||
const videoOverlay = document.getElementById('video-overlay');
|
const videoOverlay = document.getElementById('video-overlay');
|
||||||
@@ -346,115 +778,135 @@ const checkBackdropAndLogo = movie => {
|
|||||||
).catch(() => fetchRandomMovie());
|
).catch(() => fetchRandomMovie());
|
||||||
};
|
};
|
||||||
|
|
||||||
const readCustomList = () =>
|
const readCustomList = async () => {
|
||||||
fetch(listFileName + '?' + new Date().getTime())
|
const currentListFile = await getSeasonalListFileName();
|
||||||
|
return fetch(currentListFile + '?' + new Date().getTime())
|
||||||
.then(response => response.ok ? response.text() : null)
|
.then(response => response.ok ? response.text() : null)
|
||||||
.then(text => {
|
.then(text => {
|
||||||
if (!text) return null;
|
if (!text || !text.trim()) {
|
||||||
const lines = text.split('\n').filter(Boolean);
|
console.warn('List.txt is empty or could not be loaded.');
|
||||||
title = lines.shift() || title;
|
return null; // Fallback to random selection
|
||||||
return lines.map(line => line.trim().substring(0, 32));
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
|
|
||||||
// using Fisher-Yates shuffle algorithm if list is available and setRandomMovie is set to true
|
|
||||||
const shuffleArray = (array) => {
|
|
||||||
for (let i = array.length - 1; i > 0; i--) {
|
|
||||||
// Generate a random index between 0 and i
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
// Swap elements at indices i and j
|
|
||||||
[array[i], array[j]] = [array[j], array[i]];
|
|
||||||
|
|
||||||
//var temp = array[i];
|
|
||||||
//array[i] = array[j];
|
|
||||||
//array[j] = temp;
|
|
||||||
}
|
}
|
||||||
return array;
|
const lines = text.split('\n').filter(Boolean);
|
||||||
|
|
||||||
|
const firstLine = lines.shift().trim();
|
||||||
|
const tokens = firstLine.split(/\s+/).filter(Boolean);
|
||||||
|
|
||||||
|
let muteSetting = null;
|
||||||
|
if (tokens.length > 0) {
|
||||||
|
const lastToken = tokens[tokens.length - 1].toLowerCase();
|
||||||
|
if (lastToken === 'muteon' || lastToken === 'muteoff') {
|
||||||
|
muteSetting = lastToken;
|
||||||
|
tokens.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedTitle = tokens.join(' ').trim();
|
||||||
|
if (parsedTitle) {
|
||||||
|
title = parsedTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (muteSetting) {
|
||||||
|
startTrailerMuted = muteSetting === 'muteon';
|
||||||
|
}
|
||||||
|
|
||||||
|
isMuted = startTrailerMuted;
|
||||||
|
|
||||||
|
// Remaining lines are media IDs
|
||||||
|
const mediaList = lines.map(line => line.trim().substring(0, 32));
|
||||||
|
if (setRandomMovie) {
|
||||||
|
return shuffleArray(mediaList); // Shuffle the list before returning it if set
|
||||||
|
}
|
||||||
|
return mediaList; // return exact list
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.error('Error reading List.txt. Falling back to random selection.');
|
||||||
|
return null;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//const shuffleArray = (array) => array.sort(() => Math.random() - 0.5); //better use Fisher-Yates shuffle algorithm
|
|
||||||
|
|
||||||
const fetchRandomMovie = () => {
|
const fetchRandomMovie = () => {
|
||||||
if (isChangingSlide) return;
|
if (isChangingSlide) return;
|
||||||
isChangingSlide = true;
|
isChangingSlide = true;
|
||||||
|
|
||||||
if (movieList.length === 0) {
|
if (movieList.length === 0) {
|
||||||
readCustomList().then(list => {
|
readCustomList().then(list => {
|
||||||
if (list) {
|
if (list && list.length > 0) {
|
||||||
movieList = list;
|
movieList = list;
|
||||||
//// Shuffle the list if it was set by the user
|
|
||||||
//if (setRandomMovie) {
|
|
||||||
// shuffleArray(movieList);
|
|
||||||
//}
|
|
||||||
currentMovieIndex = 0;
|
currentMovieIndex = 0;
|
||||||
}
|
|
||||||
fetchNextMovie();
|
fetchNextMovie();
|
||||||
|
} else {
|
||||||
|
console.warn("Fallback to random selection.");
|
||||||
|
fetchNextMovie(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else fetchNextMovie();
|
} else {
|
||||||
|
fetchNextMovie();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchNextMovie = () => {
|
const shuffleArray = (array) => {
|
||||||
const fetchCurrentUserId = () =>
|
for (let i = array.length - 1; i > 0; i--) {
|
||||||
fetch('/Sessions', {
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
headers: { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` }
|
[array[i], array[j]] = [array[j], array[i]];
|
||||||
})
|
}
|
||||||
.then(response => response.json())
|
return array;
|
||||||
.then(sessions => {
|
};
|
||||||
const currentSession = sessions.find(session => session.UserId);
|
|
||||||
return currentSession ? currentSession.UserId : null;
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
|
|
||||||
fetchCurrentUserId().then(currentUserId => {
|
const fetchNextMovie = (useRandom = false) => {
|
||||||
if (!currentUserId) {
|
if (!userId) {
|
||||||
console.error('Could not retrieve the current user ID.');
|
console.error('Could not retrieve the current user ID.');
|
||||||
|
isChangingSlide = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` };
|
const headers = { 'Authorization': `MediaBrowser Client="Jellyfin Web", Device="YourDeviceName", DeviceId="YourDeviceId", Version="YourClientVersion", Token="${token}"` };
|
||||||
|
|
||||||
if (movieList.length > 0) {
|
if (!useRandom && movieList.length > 0) {
|
||||||
if (currentMovieIndex >= movieList.length) currentMovieIndex = 0;
|
if (currentMovieIndex >= movieList.length) currentMovieIndex = 0;
|
||||||
const movieId = movieList[currentMovieIndex];
|
const movieId = movieList[currentMovieIndex];
|
||||||
currentMovieIndex++;
|
currentMovieIndex++;
|
||||||
|
|
||||||
fetch(`/Users/${currentUserId}/Items/${movieId}?Fields=Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
fetch(`/Users/${userId}/Items/${movieId}?Fields=Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(checkBackdropAndLogo)
|
.then(checkBackdropAndLogo)
|
||||||
.catch(() => startSlideChangeTimer())
|
.catch(() => startSlideChangeTimer())
|
||||||
.finally(() => { isChangingSlide = false; });
|
.finally(() => { isChangingSlide = false; });
|
||||||
} else {
|
} else {
|
||||||
const itemTypes = moviesSeriesBoth === 1 ? 'Movie' : (moviesSeriesBoth === 2 ? 'Series' : 'Movie,Series');
|
const itemTypes = moviesSeriesBoth === 1 ? 'Movie' : (moviesSeriesBoth === 2 ? 'Series' : 'Movie,Series');
|
||||||
fetch(`/Users/${currentUserId}/Items?IncludeItemTypes=${itemTypes}&Recursive=true&Limit=1&SortBy=random&Fields=Id,Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
fetch(`/Users/${userId}/Items?IncludeItemTypes=${itemTypes}&Recursive=true&Limit=1&SortBy=random&Fields=Id,Overview,RemoteTrailers,PremiereDate,RunTimeTicks,ChildCount,Genres`, { headers })
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => { if (data.Items[0]) checkBackdropAndLogo(data.Items[0]); })
|
.then(data => { if (data.Items[0]) checkBackdropAndLogo(data.Items[0]); })
|
||||||
.catch(() => startSlideChangeTimer())
|
.catch(() => startSlideChangeTimer())
|
||||||
.finally(() => { isChangingSlide = false; });
|
.finally(() => { isChangingSlide = false; });
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkNavigation = () => {
|
const checkNavigation = () => {
|
||||||
const newLocation = window.top.location.href;
|
const newLocation = window.top.location.href;
|
||||||
// Check if the user is navigating to or from the homepage
|
|
||||||
if (newLocation !== currentLocation) {
|
if (newLocation !== currentLocation) {
|
||||||
currentLocation = newLocation;
|
currentLocation = newLocation;
|
||||||
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
||||||
if (isHomePage(newLocation)) {
|
|
||||||
if (!isHomePageActive) {
|
if (!isHomePage(newLocation) && isHomePageActive) {
|
||||||
console.log("Returning to homepage, reactivating slideshow");
|
console.log("Leaving home page, cleaning up slideshow and stopping video");
|
||||||
isHomePageActive = true;
|
|
||||||
cleanup();
|
|
||||||
fetchRandomMovie();
|
|
||||||
}
|
|
||||||
} else if (isHomePageActive) {
|
|
||||||
console.log("Leaving homepage, cleaning up slideshow");
|
|
||||||
isHomePageActive = false;
|
isHomePageActive = false;
|
||||||
|
stopBackgroundVideo();
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isHomePage(newLocation) && !isHomePageActive) {
|
||||||
|
console.log("Returning to home page, reactivating slideshow");
|
||||||
|
isHomePageActive = true;
|
||||||
|
fetchRandomMovie();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if parent is available and if the iframe is in an embedded environment
|
// Check if parent is available and if the iframe is in an embedded environment
|
||||||
if (!showOnOtherPages && window.parent && window.parent !== window) {
|
if (!showOnOtherPages && window.parent && window.parent !== window) {
|
||||||
|
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
||||||
|
if (isHomePage(newLocation)) {
|
||||||
const homeTab = window.parent.document.getElementById('homeTab');
|
const homeTab = window.parent.document.getElementById('homeTab');
|
||||||
const isHomeTabActive = homeTab && homeTab.classList.contains('is-active');
|
const isHomeTabActive = homeTab && homeTab.classList.contains('is-active');
|
||||||
|
|
||||||
@@ -463,19 +915,28 @@ const checkNavigation = () => {
|
|||||||
console.log("HomeTab is active, reactivating slideshow");
|
console.log("HomeTab is active, reactivating slideshow");
|
||||||
isHomePageActive = true;
|
isHomePageActive = true;
|
||||||
window.parent.document.querySelector('.featurediframe').style.display = 'block';
|
window.parent.document.querySelector('.featurediframe').style.display = 'block';
|
||||||
cleanup();
|
|
||||||
fetchRandomMovie();
|
fetchRandomMovie();
|
||||||
} else if (!isHomeTabActive && isHomePageActive) {
|
} else if (!isHomeTabActive && isHomePageActive) {
|
||||||
console.log("Leaving HomeTab, cleaning up slideshow");
|
console.log("Leaving HomeTab, cleaning up slideshow");
|
||||||
isHomePageActive = false;
|
isHomePageActive = false;
|
||||||
window.parent.document.querySelector('.featurediframe').style.display = 'none';
|
window.parent.document.querySelector('.featurediframe').style.display = 'none';
|
||||||
|
stopBackgroundVideo();
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error("Spotlight iframe is not in an embedded environment, has a different domain or showOnOtherPages is set to true");
|
console.error("Spotlight iframe is not in an embedded environment, has a different domain or showOnOtherPages is set to true");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stopBackgroundVideo = () => {
|
||||||
|
if (player && typeof player.stopVideo === 'function') {
|
||||||
|
player.stopVideo();
|
||||||
|
player.destroy();
|
||||||
|
}
|
||||||
|
player = null;
|
||||||
|
};
|
||||||
|
|
||||||
setInterval(checkNavigation, 60);
|
setInterval(checkNavigation, 60);
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
@@ -483,17 +944,46 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
const isHomePage = url => url.includes('/home') || url.endsWith('/web/') || url.endsWith('/web/index.html');
|
||||||
if (isHomePage(window.top.location.href)) {
|
if (isHomePage(window.top.location.href)) {
|
||||||
isHomePageActive = true;
|
isHomePageActive = true;
|
||||||
|
console.log("Home page detected, starting slideshow");
|
||||||
readCustomList().then(list => {
|
readCustomList().then(list => {
|
||||||
if (list) {
|
if (list) { movieList = list; currentMovieIndex = 0; }
|
||||||
movieList = list;
|
|
||||||
// Shuffle the list if it was set by the user
|
|
||||||
if (setRandomMovie) {
|
|
||||||
shuffleArray(movieList);
|
|
||||||
}
|
|
||||||
currentMovieIndex = 0;
|
|
||||||
}
|
|
||||||
fetchRandomMovie();
|
fetchRandomMovie();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (setMutedHover) {
|
||||||
|
const parentBody = window.parent.document.body;
|
||||||
|
const hoverContainer = document.getElementById('slides-container');
|
||||||
|
|
||||||
|
// prevent error: Unmuting failed and the element was paused instead because the user didn't interact with the document before.
|
||||||
|
const onUserInteraction = () => {
|
||||||
|
userInteracted = true;
|
||||||
|
console.log('User interacted with the page');
|
||||||
|
parentBody.removeEventListener('click', onUserInteraction);
|
||||||
|
parentBody.removeEventListener('keydown', onUserInteraction);
|
||||||
|
hoverContainer.removeEventListener('click', onUserInteraction);
|
||||||
|
hoverContainer.removeEventListener('keydown', onUserInteraction);
|
||||||
|
};
|
||||||
|
|
||||||
|
parentBody.addEventListener('click', onUserInteraction);
|
||||||
|
parentBody.addEventListener('keydown', onUserInteraction);
|
||||||
|
hoverContainer.addEventListener('click', onUserInteraction);
|
||||||
|
hoverContainer.addEventListener('keydown', onUserInteraction);
|
||||||
|
|
||||||
|
hoverContainer.addEventListener('mouseenter', () => {
|
||||||
|
if (userInteracted && player && typeof player.unMute === 'function') {
|
||||||
|
player.unMute();
|
||||||
|
isMuted = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hoverContainer.addEventListener('mouseleave', () => {
|
||||||
|
if (userInteracted && player && typeof player.mute === 'function') {
|
||||||
|
player.mute();
|
||||||
|
isMuted = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css">
|
||||||
<title>Jellyfin Spotlight v2.3.2 Fork v1.0</title>
|
<title>Jellyfin Featured Content Bar v3.4.1</title>
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
<link rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="slides-container"></div>
|
<div id="slides-container"></div>
|
||||||
<div id="video-overlay" style="display: none;">
|
<div id="video-overlay" style="display: none;">
|
||||||
@@ -15,7 +18,8 @@
|
|||||||
<iframe id="trailer-video" width="100%" height="100%" frameborder="0" allowfullscreen></iframe>
|
<iframe id="trailer-video" width="100%" height="100%" frameborder="0" allowfullscreen></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="script.js"></script>
|
|
||||||
<script src="https://www.youtube.com/iframe_api"></script>
|
<script src="https://www.youtube.com/iframe_api"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
434
styles.css
@@ -1,11 +1,31 @@
|
|||||||
@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');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--slide-heading-gap: 2.6em;
|
||||||
|
--slide-heading-accent: #38bdf8;
|
||||||
|
--slide-heading-accent-strong: #a855f7;
|
||||||
|
--slide-heading-bg: rgba(15, 23, 42, 0.45);
|
||||||
|
--slide-heading-border: rgba(148, 163, 184, 0.38);
|
||||||
|
--slide-heading-text: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slide-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100vw;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-wrapper.has-heading {
|
||||||
|
padding-top: var(--slide-heading-gap);
|
||||||
|
}
|
||||||
|
|
||||||
.slide {
|
.slide {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@@ -14,6 +34,10 @@ body {
|
|||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slide-wrapper.has-heading .slide {
|
||||||
|
height: calc(22em - var(--slide-heading-gap));
|
||||||
|
}
|
||||||
|
|
||||||
.slide:focus {
|
.slide:focus {
|
||||||
outline: 2px solid #fff;
|
outline: 2px solid #fff;
|
||||||
}
|
}
|
||||||
@@ -30,7 +54,7 @@ body {
|
|||||||
transition: width 0.5s ease, filter 0.8s ease, scale 2s ease;
|
transition: width 0.5s ease, filter 0.8s ease, scale 2s ease;
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
animation: objectPositionAnimation 45s ease-in-out infinite;
|
animation: objectPositionAnimation 45s ease-in-out infinite;
|
||||||
border-radius: 1em;
|
border-radius: 2.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
@@ -40,100 +64,82 @@ body {
|
|||||||
max-height: 9em;
|
max-height: 9em;
|
||||||
max-width: 29em;
|
max-width: 29em;
|
||||||
width: auto;
|
width: auto;
|
||||||
z-index: 3;
|
z-index: 7;
|
||||||
text-shadow: -2px 2px 4px rgba(0, 0, 0, 0.5);
|
text-shadow: -2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
filter: drop-shadow(1px 1px 1px);
|
filter: drop-shadow(1px 1px 1px);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: transform 0.3s ease, max-height 0.3s ease, max-width 0.3s ease, left 0.5s ease;
|
transition: transform 0.3s ease, max-height 0.3s ease, max-width 0.3s ease, left 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
position: absolute;
|
||||||
|
top: calc((var(--slide-heading-gap) - 2em) / 2);
|
||||||
|
left: 0em;
|
||||||
|
min-height: 2em;
|
||||||
|
padding: 0.25em 1.8em;
|
||||||
|
width: max-content;
|
||||||
|
font-family: "Titillium Web", sans-serif;
|
||||||
|
color: var(--slide-heading-text);
|
||||||
|
font-size: 1.3em;
|
||||||
|
font-weight: 620;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.5em;
|
||||||
|
z-index: 8;
|
||||||
|
background: linear-gradient(135deg, rgba(15, 23, 42, 0.82) 0%, rgba(15, 23, 42, 0.45) 100%);
|
||||||
|
border-radius: 0.75em;
|
||||||
|
border: 1px solid var(--slide-heading-border);
|
||||||
|
box-shadow: 0 16px 32px -18px rgba(15, 23, 42, 0.8), 0 0 32px -26px rgba(56, 189, 248, 0.6);
|
||||||
|
backdrop-filter: blur(18px) saturate(165%);
|
||||||
|
-webkit-backdrop-filter: blur(18px) saturate(165%);
|
||||||
|
text-shadow: 0 2px 10px rgba(15, 23, 42, 0.65);
|
||||||
|
box-sizing: border-box;
|
||||||
|
isolation: isolate;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.heading::before {
|
.heading::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2.27em;
|
inset: -35% -20% 45% -35%;
|
||||||
left: 0;
|
background: radial-gradient(circle at top left, rgba(56, 189, 248, 0.75), transparent 60%);
|
||||||
width: 100%;
|
opacity: 0.65;
|
||||||
height: 100vh;
|
filter: blur(22px);
|
||||||
background: linear-gradient(0deg, rgb(0% 0% 0%) 0%, rgb(0% 0% 0% / 0.9990234375) 6.25%, rgba(0, 0, 0, 0.99) 12.5%, rgba(0, 0, 0, 0.97) 18.75%, rgba(0, 0, 0, 0.94) 25%, rgba(0, 0, 0, 0.88) 31.25%, rgba(0, 0, 0, 0.79) 37.5%, rgba(0, 0, 0, 0.67) 43.75%, rgba(0, 0, 0, 0.5) 50%, rgba(0, 0, 0, 0.33) 56.25%, rgba(0, 0, 0, 0.21) 62.5%, rgba(0, 0, 0, 0.12) 68.75%, rgba(0, 0, 0, 0.06) 75%, rgba(0, 0, 0, 0.03) 81.25%, rgba(0, 0, 0, 0.01) 87.5%, rgba(0, 0, 0, 0) 93.75%, rgba(0, 0, 0, 0) 100%);
|
z-index: -1;
|
||||||
z-index: 7;
|
pointer-events: none;
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading::after {
|
||||||
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
left: 1.6em;
|
||||||
left: 0;
|
bottom: -0.45em;
|
||||||
width: 100%;
|
width: 3.8em;
|
||||||
height: 2.3em;
|
height: 0.26em;
|
||||||
background-color: transparent;
|
border-radius: 999px;
|
||||||
font-family: "Titillium Web", sans-serif;
|
background: linear-gradient(90deg, var(--slide-heading-accent) 0%, var(--slide-heading-accent-strong) 100%);
|
||||||
color: #D3D3D3;
|
box-shadow: 0 8px 18px rgba(56, 189, 248, 0.55);
|
||||||
font-size: 22px;
|
opacity: 0.95;
|
||||||
display: none;
|
pointer-events: none;
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
z-index: 2;
|
|
||||||
padding: 10px;
|
|
||||||
padding-left: 0px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-shadow: 1px 1px 4px rgba(0, 0, 0, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MARK: modified
|
|
||||||
css for back and skip button
|
|
||||||
*/
|
|
||||||
.back-button,
|
.back-button,
|
||||||
.skip-button {
|
.skip-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
color: #D3D3D3;
|
color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
font-family: "Titillium Web", sans-serif;
|
font-family: "Titillium Web", sans-serif;
|
||||||
text-shadow: 1px 1px 4px rgba(0, 0, 0, 1);
|
text-shadow: 1px 1px 4px rgba(0, 0, 0, 1);
|
||||||
font-style: normal;
|
font-size: 2em;
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: normal;
|
|
||||||
line-height: 1;
|
|
||||||
text-transform: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-font-feature-settings: "liga";
|
|
||||||
font-feature-settings: "liga";
|
|
||||||
border-radius: 50%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
opacity: 0.5;
|
|
||||||
transition: opacity 0.3s ease, background 0.3s ease;
|
|
||||||
font-size: 1.62em;
|
|
||||||
width: 1.7em;
|
|
||||||
height: 1.7em;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button:hover,
|
|
||||||
.skip-button:hover {
|
|
||||||
background: #ffffff70;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skip-button {
|
|
||||||
right: 0.5em;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-button {
|
.details-button {
|
||||||
left: 0.5em;
|
display: none;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
/* display: none; */ /* Optional, if activated later */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
@@ -203,7 +209,7 @@ css for back and skip button
|
|||||||
|
|
||||||
.age-rating {
|
.age-rating {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.1em;
|
top: 0.5em;
|
||||||
left: 2em;
|
left: 2em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-family: "Titillium Web", sans-serif;
|
font-family: "Titillium Web", sans-serif;
|
||||||
@@ -226,9 +232,9 @@ css for back and skip button
|
|||||||
|
|
||||||
.plot {
|
.plot {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0.1em;
|
top: -0.3em;
|
||||||
left: 2.2em;
|
left: 50vw;
|
||||||
right: 0;
|
transform: translateX(-50%);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: "Titillium Web", sans-serif;
|
font-family: "Titillium Web", sans-serif;
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
@@ -245,13 +251,14 @@ css for back and skip button
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-bottom-left-radius: 0.45em;
|
border-bottom-left-radius: 0.45em;
|
||||||
transition: opacity 0.1s ease, max-height 0.8s ease, width 0.3s ease;
|
transition: opacity 0.1s ease, max-height 0.8s ease, width 0.3s ease, left 0.3s ease;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
max-width: 96vw;
|
max-width: 96vw;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
line-clamp: 2;
|
line-clamp: 2;
|
||||||
max-height: 7.8em;
|
max-height: 7.8em;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genres {
|
.genres {
|
||||||
@@ -349,19 +356,17 @@ css for back and skip button
|
|||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .slide:hover .clickable-overlay {} */
|
/*.slide:hover .clickable-overlay {}*/
|
||||||
|
|
||||||
.slide:hover .clickable-overlay::before {
|
.slide:hover .clickable-overlay::before {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
backdrop-filter: blur(0px);
|
backdrop-filter: blur(0px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.slide:hover .logo {}
|
.slide:hover .logo {}
|
||||||
|
|
||||||
.slide:hover .plot {}
|
.slide:hover .plot {}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.slide:hover .lorem-ipsum::before {
|
.slide:hover .lorem-ipsum::before {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
@@ -376,6 +381,34 @@ css for back and skip button
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 1000px) {
|
||||||
|
:root {
|
||||||
|
--slide-heading-gap: 2.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
left: 0em;
|
||||||
|
font-size: 1.05em;
|
||||||
|
padding: 0.3em 1.1em;
|
||||||
|
border-radius: 0.65em;
|
||||||
|
gap: 0.4em;
|
||||||
|
box-shadow: 0 12px 22px -18px rgba(15, 23, 42, 0.72), 0 0 0 1px rgba(148, 163, 184, 0.18);
|
||||||
|
backdrop-filter: blur(14px) saturate(150%);
|
||||||
|
-webkit-backdrop-filter: blur(14px) saturate(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading::before {
|
||||||
|
inset: -42% -26% 48% -38%;
|
||||||
|
filter: blur(16px);
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading::after {
|
||||||
|
left: 1em;
|
||||||
|
bottom: -0.32em;
|
||||||
|
width: 2.6em;
|
||||||
|
height: 0.22em;
|
||||||
|
}
|
||||||
|
|
||||||
.age-rating {
|
.age-rating {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1em;
|
top: 1em;
|
||||||
@@ -393,7 +426,7 @@ css for back and skip button
|
|||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
transform: translateX(-50%) translateY(-50%);
|
transform: translateX(-50%) translateY(-50%);
|
||||||
top: 10.5em;
|
top: 6.5em;
|
||||||
left: 50% !important;
|
left: 50% !important;
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
max-height: 45%;
|
max-height: 45%;
|
||||||
@@ -490,11 +523,7 @@ css for back and skip button
|
|||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
z-index: 0;
|
z-index: 9;
|
||||||
}
|
|
||||||
|
|
||||||
.heading::before {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-container {
|
.video-container {
|
||||||
@@ -567,12 +596,15 @@ css for back and skip button
|
|||||||
|
|
||||||
@media (max-width:1000px) and (orientation:portrait) {
|
@media (max-width:1000px) and (orientation:portrait) {
|
||||||
.back-button {
|
.back-button {
|
||||||
left: 0em;
|
top: 5em !important;
|
||||||
|
left: 0.5em;
|
||||||
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip-button {
|
.skip-button {
|
||||||
right: 0em;
|
top: 5em !important;
|
||||||
/*top: 50% !important;*/
|
right: 0.5em;
|
||||||
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genres {
|
.genres {
|
||||||
@@ -593,6 +625,10 @@ css for back and skip button
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width:1000px) and (orientation:landscape) {
|
@media (max-width:1000px) and (orientation:landscape) {
|
||||||
|
.slide-wrapper.has-heading .slide {
|
||||||
|
height: calc(22em - var(--slide-heading-gap));
|
||||||
|
}
|
||||||
|
|
||||||
.community-rating {
|
.community-rating {
|
||||||
left: 89vw !important;
|
left: 89vw !important;
|
||||||
}
|
}
|
||||||
@@ -602,12 +638,15 @@ css for back and skip button
|
|||||||
}
|
}
|
||||||
|
|
||||||
.back-button {
|
.back-button {
|
||||||
left: 0em;
|
top: 5em !important;
|
||||||
|
left: 0.5em;
|
||||||
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip-button {
|
.skip-button {
|
||||||
right: 0em;
|
top: 5em !important;
|
||||||
/*top: 50% !important;*/
|
right: 0.5em;
|
||||||
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genres {
|
.genres {
|
||||||
@@ -628,43 +667,85 @@ css for back and skip button
|
|||||||
@media (min-width: 1001px) {
|
@media (min-width: 1001px) {
|
||||||
.logo {
|
.logo {
|
||||||
max-height: 45%;
|
max-height: 45%;
|
||||||
top: 50vh;
|
top: 34vh;
|
||||||
max-width: 47em;
|
max-width: 47em;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transition: transform 0.1s ease, max-height 0.3s ease;
|
transition: transform 0.1s ease, left 0.3s ease, max-height 0.3s ease;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.watch-trailer-button {
|
.lorem-ipsum {
|
||||||
|
left: 50vw;
|
||||||
|
transition: left 0.3s ease;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: unset;
|
||||||
|
bottom: 3.2em;
|
||||||
|
padding-left: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.watch-trailer-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backdrop {
|
.backdrop {
|
||||||
left: 0em;
|
left: 0em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: width 0.5s ease, left 0.5s ease, filter 0.8s ease, transform 2s ease;
|
transition: width 0.5s ease, left 0.5s ease, filter 0.8s ease, transform 2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.genres {
|
.genres {
|
||||||
left: 8em;
|
left: 50vw;
|
||||||
|
top: unset;
|
||||||
|
bottom: 5em;
|
||||||
|
transition: left 0.3s;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
max-width: 39em;
|
max-width: 39em;
|
||||||
}
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.community-rating {
|
.community-rating {
|
||||||
top: 16.45em;
|
top: 16.45em;
|
||||||
left: 31em;
|
left: 31em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.critic-rating {
|
.critic-rating {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 16.45em;
|
top: 16.45em;
|
||||||
left: 26.5em;
|
left: 26.5em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container::before {
|
.back-button {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1.2em;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: background 0.3s, color 0.3s;
|
||||||
|
color: #ffffffa1;
|
||||||
|
left: 0.5em;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-button {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 1.2em;
|
||||||
|
height: 1.2em;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: background 0.3s, color 0.3s;
|
||||||
|
color: #ffffffa1;
|
||||||
|
right: 0.5em;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-container::before {
|
||||||
content: '';
|
content: '';
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -672,30 +753,40 @@ css for back and skip button
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
top: -19.3em;
|
top: -19.3em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.7) 6.25%, rgba(0, 0, 0, 0.68) 12.5%, rgba(0, 0, 0, 0.66) 18.75%, rgba(0, 0, 0, 0.64) 25%, rgba(0, 0, 0, 0.6) 31.25%, rgba(0, 0, 0, 0.56) 37.5%, rgba(0, 0, 0, 0.51) 43.75%, rgba(0, 0, 0, 0.45) 50%, rgba(0, 0, 0, 0.38) 56.25%, rgba(0, 0, 0, 0.31) 62.5%, rgba(0, 0, 0, 0.22) 68.75%, rgba(0, 0, 0, 0.14) 75%, rgba(0, 0, 0, 0.13) 81.25%, rgba(0, 0, 0, 0) 100%);
|
background: transparent;
|
||||||
opacity: 0.8;
|
backdrop-filter: blur(0px);
|
||||||
backdrop-filter: blur(1px);
|
|
||||||
border-top-right-radius: 1em;
|
border-top-right-radius: 1em;
|
||||||
border-top-left-radius: 1em;
|
border-top-left-radius: 1em;
|
||||||
-webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
-webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
||||||
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-container::after {
|
.text-container::after {
|
||||||
content: '';
|
content: '';
|
||||||
background: linear-gradient(0deg, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.7) 6.25%, rgba(0, 0, 0, 0.68) 12.5%, rgba(0, 0, 0, 0.66) 18.75%, rgba(0, 0, 0, 0.64) 25%, rgba(0, 0, 0, 0.6) 31.25%, rgba(0, 0, 0, 0.56) 37.5%, rgba(0, 0, 0, 0.51) 43.75%, rgba(0, 0, 0, 0.45) 50%, rgba(0, 0, 0, 0.38) 56.25%, rgba(0, 0, 0, 0.31) 62.5%, rgba(0, 0, 0, 0.22) 68.75%, rgba(0, 0, 0, 0.14) 75%, rgba(0, 0, 0, 0.13) 81.25%, rgba(0, 0, 0, 0) 100%);
|
background: linear-gradient(0deg, rgb(0% 0% 0% / 0.98) 0%, rgb(0% 0% 0% / 0.9705847873975829) 6.25%, rgb(0% 0% 0% / 0.9427009709305305) 12.5%, rgb(0% 0% 0% / 0.8974201100282472) 18.75%, rgb(0% 0% 0% / 0.8364823227814083) 25%, rgb(0% 0% 0% / 0.7622294141796051) 31.25%, rgb(0% 0% 0% / 0.6775148818588941) 37.5%, rgb(0% 0% 0% / 0.5855942577879029) 43.75%, rgb(0% 0% 0% / 0.49000000000000005) 50%, rgb(0% 0% 0% / 0.39440574221209723) 56.25%, rgb(0% 0% 0% / 0.3024851181411061) 62.5%, rgb(0% 0% 0% / 0.217770585820395) 68.75%, rgb(0% 0% 0% / 0.14351767721859177) 75%, rgb(0% 0% 0% / 0.0825798899717528) 81.25%, rgb(0% 0% 0% / 0.03729902906946947) 87.5%, rgb(0% 0% 0% / 0.009415212602417067) 93.75%, rgb(0% 0% 0% / 0) 100%);
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
border-top-right-radius: 1em;
|
border-bottom-left-radius: 1em;
|
||||||
border-top-left-radius: 1em;
|
|
||||||
bottom: 0em;
|
bottom: 0em;
|
||||||
height: 3em;
|
height: 22.5em;
|
||||||
opacity: 0.8;
|
opacity: 1;
|
||||||
backdrop-filter: blur(1px);
|
backdrop-filter: blur(0px);
|
||||||
-webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
-webkit-mask-image: linear-gradient(0deg, rgb(0% 0% 0% / 0.98) 0%, rgb(0% 0% 0% / 0.9705847873975829) 6.25%, rgb(0% 0% 0% / 0.9427009709305305) 12.5%, rgb(0% 0% 0% / 0.8974201100282472) 18.75%, rgb(0% 0% 0% / 0.8364823227814083) 25%, rgb(0% 0% 0% / 0.7622294141796051) 31.25%, rgb(0% 0% 0% / 0.6775148818588941) 37.5%, rgb(0% 0% 0% / 0.5855942577879029) 43.75%, rgb(0% 0% 0% / 0.49000000000000005) 50%, rgb(0% 0% 0% / 0.39440574221209723) 56.25%, rgb(0% 0% 0% / 0.3024851181411061) 62.5%, rgb(0% 0% 0% / 0.217770585820395) 68.75%, rgb(0% 0% 0% / 0.14351767721859177) 75%, rgb(0% 0% 0% / 0.0825798899717528) 81.25%, rgb(0% 0% 0% / 0.03729902906946947) 87.5%, rgb(0% 0% 0% / 0.009415212602417067) 93.75%, rgb(0% 0% 0% / 0) 100%);
|
||||||
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.25) 3%, rgba(0, 0, 0, 0.5) 7%, rgba(0, 0, 0, 0.75) 15%, rgba(0, 0, 0, 0.9) 25%, rgba(0, 0, 0, 1) 35%, rgba(0, 0, 0, 1) 100%);
|
mask-image: linear-gradient(0deg, rgb(0% 0% 0% / 0.98) 0%, rgb(0% 0% 0% / 0.9705847873975829) 6.25%, rgb(0% 0% 0% / 0.9427009709305305) 12.5%, rgb(0% 0% 0% / 0.8974201100282472) 18.75%, rgb(0% 0% 0% / 0.8364823227814083) 25%, rgb(0% 0% 0% / 0.7622294141796051) 31.25%, rgb(0% 0% 0% / 0.6775148818588941) 37.5%, rgb(0% 0% 0% / 0.5855942577879029) 43.75%, rgb(0% 0% 0% / 0.49000000000000005) 50%, rgb(0% 0% 0% / 0.39440574221209723) 56.25%, rgb(0% 0% 0% / 0.3024851181411061) 62.5%, rgb(0% 0% 0% / 0.217770585820395) 68.75%, rgb(0% 0% 0% / 0.14351767721859177) 75%, rgb(0% 0% 0% / 0.0825798899717528) 81.25%, rgb(0% 0% 0% / 0.03729902906946947) 87.5%, rgb(0% 0% 0% / 0.009415212602417067) 93.75%, rgb(0% 0% 0% / 0) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-button:hover {
|
||||||
|
background: #101010a8;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button:hover {
|
||||||
|
background: #101010a8;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.age-rating {
|
.age-rating {
|
||||||
@@ -876,18 +967,11 @@ css for back and skip button
|
|||||||
font-size: 133%;
|
font-size: 133%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .backdrop {}
|
/*
|
||||||
|
.backdrop {}
|
||||||
|
|
||||||
.age-rating {}
|
.age-rating {}
|
||||||
|
|
||||||
.back-button {
|
|
||||||
top: 38vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skip-button {
|
|
||||||
top: 38vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-container {}
|
.video-container {}
|
||||||
|
|
||||||
.text-container::before {}
|
.text-container::before {}
|
||||||
@@ -896,14 +980,11 @@ css for back and skip button
|
|||||||
|
|
||||||
.lorem-ipsum {}
|
.lorem-ipsum {}
|
||||||
*/
|
*/
|
||||||
.logo {
|
|
||||||
top: 38vh;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 3000px) {
|
@media (min-width: 3000px) {
|
||||||
.slide {
|
.slide {
|
||||||
font-size: 200%;
|
font-size: 140%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -923,10 +1004,11 @@ css for back and skip button
|
|||||||
|
|
||||||
#video-overlay {
|
#video-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 13vh;
|
top: 10vh;
|
||||||
|
border-radius: 1em;
|
||||||
left: 0vw;
|
left: 0vw;
|
||||||
width: 100%;
|
width: 94.5vw;
|
||||||
height: 140vh;
|
height: 55vh;
|
||||||
background-color: rgb(0, 0, 0);
|
background-color: rgb(0, 0, 0);
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -938,12 +1020,30 @@ css for back and skip button
|
|||||||
|
|
||||||
#video-overlay-content {
|
#video-overlay-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 90%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 60%;
|
height: 100%;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: portrait) {
|
||||||
|
#video-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 10vh;
|
||||||
|
border-radius: 1em;
|
||||||
|
left: 0vw;
|
||||||
|
width: 87.5vw;
|
||||||
|
height: 68vh;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
background-size: cover;
|
||||||
|
padding: 6vh;
|
||||||
|
padding-top: 10vh;
|
||||||
|
margin-top: -10vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#close-overlay {
|
#close-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -1em;
|
top: -1em;
|
||||||
@@ -999,3 +1099,63 @@ css for back and skip button
|
|||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1.5s ease-in forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plot {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in forwards;
|
||||||
|
animation-delay: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.genres {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in forwards;
|
||||||
|
animation-delay: 1.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lorem-ipsum {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in forwards;
|
||||||
|
animation-delay: 1.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-container::after {
|
||||||
|
opacity: 0.7;
|
||||||
|
animation: fadeIn 1.5s ease-in forwards;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||