mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Add Christmas seasonal sections and theme to Home Screen
This commit is contained in:
1 parent
138a3be8a3
commit
4d6fbbfa7a
7 files changed
+276
-48
No files matched your search
@@ -589,6 +589,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||||||
- **[BobHasNoSoul](https://github.com/BobHasNoSoul)**: For the jellyfin-updoot functionality
|
- **[BobHasNoSoul](https://github.com/BobHasNoSoul)**: For the jellyfin-updoot functionality
|
||||||
- **[The Jellyfin Community](https://discord.gg/v7P9CAvCKZhttps://discord.gg/v7P9CAvCKZ)**: For sharing knowledge and providing a welcoming environment to ask questions and learn
|
- **[The Jellyfin Community](https://discord.gg/v7P9CAvCKZhttps://discord.gg/v7P9CAvCKZ)**: For sharing knowledge and providing a welcoming environment to ask questions and learn
|
||||||
- **[movie-monk-b0t](https://github.com/movie-monk-b0t)**: For the IMDb Top 250 JSON data
|
- **[movie-monk-b0t](https://github.com/movie-monk-b0t)**: For the IMDb Top 250 JSON data
|
||||||
|
- **[joshmcrty](https://github.com/joshmcrty)**: For the [Flurry](https://github.com/joshmcrty/Flurry) jQuery plugin
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
@@ -608,6 +609,8 @@ This README and the inline comments in kefinTweaks.js and the other scripts is t
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
### Version History
|
### Version History
|
||||||
|
- **v0.3.5**: Critical bug fix
|
||||||
|
- **v0.3.4**: Improved handling of Latest/Development branches for KefinTweaks installation. Improved collection caching.
|
||||||
- **v0.3.3**: Important fix for a bug preventing the plugin from running on non-admin accounts
|
- **v0.3.3**: Important fix for a bug preventing the plugin from running on non-admin accounts
|
||||||
- **v0.3.2**: Add proper installation and uninstallation support from the Plugins page. New feature: Playlist sorting.
|
- **v0.3.2**: Add proper installation and uninstallation support from the Plugins page. New feature: Playlist sorting.
|
||||||
- **v0.3.1**: New Home Screen Section feature: Spotlights! Improved Configuration UI.
|
- **v0.3.1**: New Home Screen Section feature: Spotlights! Improved Configuration UI.
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ window.KefinTweaksDefaultConfig = {
|
|||||||
"sortOrder": "ReleaseDate",
|
"sortOrder": "ReleaseDate",
|
||||||
"sortOrderDirection": "Descending",
|
"sortOrderDirection": "Descending",
|
||||||
"cardFormat": "Poster",
|
"cardFormat": "Poster",
|
||||||
"order": 30
|
"order": 30,
|
||||||
|
"minPremiereDate": "",
|
||||||
|
"maxPremiereDate": ""
|
||||||
},
|
},
|
||||||
"episodes": {
|
"episodes": {
|
||||||
"name": "Recently Aired Episodes",
|
"name": "Recently Aired Episodes",
|
||||||
@@ -42,7 +44,9 @@ window.KefinTweaksDefaultConfig = {
|
|||||||
"sortOrder": "PremiereDate",
|
"sortOrder": "PremiereDate",
|
||||||
"sortOrderDirection": "Descending",
|
"sortOrderDirection": "Descending",
|
||||||
"cardFormat": "Backdrop",
|
"cardFormat": "Backdrop",
|
||||||
"order": 31
|
"order": 31,
|
||||||
|
"minPremiereDate": "",
|
||||||
|
"maxPremiereDate": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trending": {
|
"trending": {
|
||||||
@@ -100,6 +104,7 @@ window.KefinTweaksDefaultConfig = {
|
|||||||
},
|
},
|
||||||
"seasonal": {
|
"seasonal": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"enableSeasonalAnimations": true,
|
||||||
"defaultItemLimit": 16,
|
"defaultItemLimit": 16,
|
||||||
"defaultSortOrder": "Random",
|
"defaultSortOrder": "Random",
|
||||||
"defaultCardFormat": "Poster",
|
"defaultCardFormat": "Poster",
|
||||||
@@ -203,28 +208,42 @@ window.KefinTweaksDefaultConfig = {
|
|||||||
"endDate": "12-31",
|
"endDate": "12-31",
|
||||||
"sections": [
|
"sections": [
|
||||||
{
|
{
|
||||||
"id": "christmas-genre",
|
"id": "seasonal-1-section-0",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"name": "Christmas Movies",
|
"name": "Christmas Movies",
|
||||||
"type": "Genre",
|
"type": "Tag",
|
||||||
"source": "Christmas",
|
"source": "christmas",
|
||||||
"itemLimit": 16,
|
"itemLimit": 16,
|
||||||
"sortOrder": "Random",
|
"sortOrder": "Random",
|
||||||
"sortOrderDirection": "Ascending",
|
"sortOrderDirection": "Ascending",
|
||||||
"cardFormat": "Poster",
|
"cardFormat": "Poster",
|
||||||
"order": 60
|
"order": 50,
|
||||||
|
"spotlight": true,
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"searchTerm": "",
|
||||||
|
"includeItemTypes": [
|
||||||
|
"Movie"
|
||||||
|
],
|
||||||
|
"additionalQueryOptions": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "christmas-family",
|
"id": "seasonal-1-section-1",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"name": "Family Movies",
|
"name": "Christmas Episodes",
|
||||||
"type": "Genre",
|
"type": "Parent",
|
||||||
"source": "Family",
|
"source": "",
|
||||||
"itemLimit": 16,
|
"itemLimit": 16,
|
||||||
"sortOrder": "Random",
|
"sortOrder": "Random",
|
||||||
"sortOrderDirection": "Ascending",
|
"sortOrderDirection": "Ascending",
|
||||||
"cardFormat": "Poster",
|
"cardFormat": "Thumb",
|
||||||
"order": 61
|
"order": 51,
|
||||||
|
"spotlight": false,
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"searchTerm": "christmas",
|
||||||
|
"includeItemTypes": [
|
||||||
|
"Episode"
|
||||||
|
],
|
||||||
|
"additionalQueryOptions": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -354,6 +373,7 @@ window.KefinTweaksDefaultConfig = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spotlightDiscoveryChance": 0.5,
|
"spotlightDiscoveryChance": 0.5,
|
||||||
|
"renderSpotlightAboveMatching": false,
|
||||||
"spotlight": {
|
"spotlight": {
|
||||||
"autoPlay": true,
|
"autoPlay": true,
|
||||||
"interval": 5000,
|
"interval": 5000,
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 576 KiB |
+74
-32
@@ -28,7 +28,26 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
const SUPPORTED_CUSTOM_SECTION_PARAMS = {
|
const SUPPORTED_CUSTOM_SECTION_PARAMS = {
|
||||||
|
filters: { label: 'Filters', type: 'array', hint: 'IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes' },
|
||||||
|
isPlayed: { label: 'Is Played', type: 'boolean' },
|
||||||
|
person: { label: 'Person Name', type: 'string' },
|
||||||
|
personTypes: { label: 'Person Types', type: 'array', hint: 'Actor, Director, Writer, etc.' },
|
||||||
|
years: { label: 'Years', type: 'array', hint: 'Comma-separated years' },
|
||||||
|
|
||||||
|
// Dates (ISO 8601)
|
||||||
|
minPremiereDate: { label: 'Min Premiere Date', type: 'date', hint: 'YYYY-MM-DD' },
|
||||||
|
maxPremiereDate: { label: 'Max Premiere Date', type: 'date', hint: 'YYYY-MM-DD' },
|
||||||
|
//minDateLastSaved: { label: 'Min Date Last Saved', type: 'date', hint: 'YYYY-MM-DD' },
|
||||||
|
//minDateLastSavedForUser: { label: 'Min Date Last Saved For User', type: 'date', hint: 'YYYY-MM-DD' },
|
||||||
|
|
||||||
|
// Numbers
|
||||||
|
indexNumber: { label: 'Index Number', type: 'number' },
|
||||||
|
parentIndexNumber: { label: 'Parent Index Number', type: 'number' },
|
||||||
|
minCommunityRating: { label: 'Min Community Rating', type: 'number' },
|
||||||
|
minCriticRating: { label: 'Min Critic Rating', type: 'number' },
|
||||||
|
|
||||||
// Booleans
|
// Booleans
|
||||||
|
isFavorite: { label: 'Is Favorite', type: 'boolean' },
|
||||||
hasThemeSong: { label: 'Has Theme Song', type: 'boolean' },
|
hasThemeSong: { label: 'Has Theme Song', type: 'boolean' },
|
||||||
hasThemeVideo: { label: 'Has Theme Video', type: 'boolean' },
|
hasThemeVideo: { label: 'Has Theme Video', type: 'boolean' },
|
||||||
hasSubtitles: { label: 'Has Subtitles', type: 'boolean' },
|
hasSubtitles: { label: 'Has Subtitles', type: 'boolean' },
|
||||||
@@ -39,45 +58,26 @@
|
|||||||
is4K: { label: 'Is 4K', type: 'boolean' },
|
is4K: { label: 'Is 4K', type: 'boolean' },
|
||||||
isMissing: { label: 'Is Missing', type: 'boolean' },
|
isMissing: { label: 'Is Missing', type: 'boolean' },
|
||||||
isUnaired: { label: 'Is Unaired', type: 'boolean' },
|
isUnaired: { label: 'Is Unaired', type: 'boolean' },
|
||||||
|
//is3D: { label: 'Is 3D', type: 'boolean' },
|
||||||
|
//isSeries: { label: 'Is Series (Live TV)', type: 'boolean' },
|
||||||
hasOverview: { label: 'Has Overview', type: 'boolean' },
|
hasOverview: { label: 'Has Overview', type: 'boolean' },
|
||||||
isFavorite: { label: 'Is Favorite', type: 'boolean' },
|
|
||||||
isPlayed: { label: 'Is Played', type: 'boolean' },
|
|
||||||
hasOfficialRating: { label: 'Has Official Rating', type: 'boolean' },
|
hasOfficialRating: { label: 'Has Official Rating', type: 'boolean' },
|
||||||
recursive: { label: 'Recursive', type: 'boolean', default: true },
|
recursive: { label: 'Recursive', type: 'boolean', default: true },
|
||||||
is3D: { label: 'Is 3D', type: 'boolean' },
|
|
||||||
isSeries: { label: 'Is Series (Live TV)', type: 'boolean' },
|
|
||||||
|
|
||||||
// Strings
|
// Strings
|
||||||
maxOfficialRating: { label: 'Max Official Rating', type: 'string', hint: 'e.g., PG-13, TV-MA' },
|
maxOfficialRating: { label: 'Max Official Rating', type: 'string', hint: 'e.g., PG-13, TV-MA' },
|
||||||
minOfficialRating: { label: 'Min Official Rating', type: 'string', hint: 'e.g., PG, TV-14' },
|
minOfficialRating: { label: 'Min Official Rating', type: 'string', hint: 'e.g., PG, TV-14' },
|
||||||
person: { label: 'Person Name', type: 'string' },
|
|
||||||
nameStartsWith: { label: 'Name Starts With', type: 'string' },
|
nameStartsWith: { label: 'Name Starts With', type: 'string' },
|
||||||
nameStartsWithOrGreater: { label: 'Name Starts With Or Greater', type: 'string' },
|
nameStartsWithOrGreater: { label: 'Name Starts With Or Greater', type: 'string' },
|
||||||
nameLessThan: { label: 'Name Less Than', type: 'string' },
|
nameLessThan: { label: 'Name Less Than', type: 'string' },
|
||||||
|
|
||||||
// Numbers
|
|
||||||
indexNumber: { label: 'Index Number', type: 'number' },
|
|
||||||
parentIndexNumber: { label: 'Parent Index Number', type: 'number' },
|
|
||||||
minCommunityRating: { label: 'Min Community Rating', type: 'number' },
|
|
||||||
minCriticRating: { label: 'Min Critic Rating', type: 'number' },
|
|
||||||
limit: { label: 'Limit', type: 'number' },
|
|
||||||
|
|
||||||
// Dates (ISO 8601)
|
|
||||||
minPremiereDate: { label: 'Min Premiere Date', type: 'date', hint: 'YYYY-MM-DD' },
|
|
||||||
maxPremiereDate: { label: 'Max Premiere Date', type: 'date', hint: 'YYYY-MM-DD' },
|
|
||||||
minDateLastSaved: { label: 'Min Date Last Saved', type: 'date', hint: 'YYYY-MM-DD' },
|
|
||||||
minDateLastSavedForUser: { label: 'Min Date Last Saved For User', type: 'date', hint: 'YYYY-MM-DD' },
|
|
||||||
|
|
||||||
// Arrays (Comma-separated strings in UI)
|
// Arrays (Comma-separated strings in UI)
|
||||||
locationTypes: { label: 'Location Types', type: 'array', hint: 'FileSystem, Remote, Virtual, Offline' },
|
locationTypes: { label: 'Location Types', type: 'array', hint: 'FileSystem, Remote, Virtual, Offline' },
|
||||||
excludeLocationTypes: { label: 'Exclude Location Types', type: 'array', hint: 'FileSystem, Remote, Virtual, Offline' },
|
excludeLocationTypes: { label: 'Exclude Location Types', type: 'array', hint: 'FileSystem, Remote, Virtual, Offline' },
|
||||||
excludeItemIds: { label: 'Exclude Item IDs', type: 'array', hint: 'Comma-separated GUIDs' },
|
excludeItemIds: { label: 'Exclude Item IDs', type: 'array', hint: 'Comma-separated GUIDs' },
|
||||||
excludeItemTypes: { label: 'Exclude Item Types', type: 'array', hint: 'Movie, Series, Episode, etc.' },
|
excludeItemTypes: { label: 'Exclude Item Types', type: 'array', hint: 'Movie, Series, Episode, etc.' },
|
||||||
filters: { label: 'Filters', type: 'array', hint: 'IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes' },
|
|
||||||
imageTypes: { label: 'Image Types', type: 'array', hint: 'Primary, Backdrop, Thumb, Logo, etc.' },
|
imageTypes: { label: 'Image Types', type: 'array', hint: 'Primary, Backdrop, Thumb, Logo, etc.' },
|
||||||
officialRatings: { label: 'Official Ratings', type: 'array', hint: 'PG, PG-13, R, etc.' },
|
officialRatings: { label: 'Official Ratings', type: 'array', hint: 'PG, PG-13, R, etc.' },
|
||||||
years: { label: 'Years', type: 'array', hint: 'Comma-separated years' },
|
|
||||||
personTypes: { label: 'Person Types', type: 'array', hint: 'Actor, Director, Writer, etc.' },
|
|
||||||
studios: { label: 'Studios', type: 'array', hint: 'Pipe-delimited names' },
|
studios: { label: 'Studios', type: 'array', hint: 'Pipe-delimited names' },
|
||||||
artists: { label: 'Artists', type: 'array', hint: 'Pipe-delimited names' },
|
artists: { label: 'Artists', type: 'array', hint: 'Pipe-delimited names' },
|
||||||
albums: { label: 'Albums', type: 'array', hint: 'Pipe-delimited names' },
|
albums: { label: 'Albums', type: 'array', hint: 'Pipe-delimited names' },
|
||||||
@@ -1041,7 +1041,7 @@
|
|||||||
function buildAdditionalOptionsControls(prefix, sectionIndex, sanitizedPrefix) {
|
function buildAdditionalOptionsControls(prefix, sectionIndex, sanitizedPrefix) {
|
||||||
// Build dropdown options from supported params
|
// Build dropdown options from supported params
|
||||||
const dropdownOptions = Object.entries(SUPPORTED_CUSTOM_SECTION_PARAMS)
|
const dropdownOptions = Object.entries(SUPPORTED_CUSTOM_SECTION_PARAMS)
|
||||||
.sort((a, b) => a[1].label.localeCompare(b[1].label))
|
//.sort((a, b) => a[1].label.localeCompare(b[1].label))
|
||||||
.map(([key, meta]) => `<option value="${key}">${meta.label}</option>`)
|
.map(([key, meta]) => `<option value="${key}">${meta.label}</option>`)
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
@@ -1667,8 +1667,23 @@
|
|||||||
<div style="margin-top: 0.75em; margin-bottom: 0.75em;">
|
<div style="margin-top: 0.75em; margin-bottom: 0.75em;">
|
||||||
${buildJellyfinCheckbox('homeScreen_recentlyReleased_movies_enabled', (recentlyReleased.movies || {}).enabled !== false, 'Enabled')}
|
${buildJellyfinCheckbox('homeScreen_recentlyReleased_movies_enabled', (recentlyReleased.movies || {}).enabled !== false, 'Enabled')}
|
||||||
</div>
|
</div>
|
||||||
<div class="listItemContent" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 0.75em;">
|
<div class="listItemContent" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75em;">
|
||||||
${createSectionConfiguration('homeScreen_recentlyReleased_movies', recentlyReleased.movies || {}, { includeName: true, defaultName: 'Recently Released Movies', includeSortOrder: false, includeOrder: true, includeIsPlayed: true })}
|
${createSectionConfiguration('homeScreen_recentlyReleased_movies', recentlyReleased.movies || {}, { includeName: true, defaultName: 'Recently Released Movies', includeSortOrder: false, includeOrder: true, includeIsPlayed: true })}
|
||||||
|
|
||||||
|
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em;">
|
||||||
|
<div class="listItemContent">
|
||||||
|
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Min Premiere Date</div>
|
||||||
|
<input type="date" id="homeScreen_recentlyReleased_movies_minPremiereDate" class="fld emby-input" value="${recentlyReleased.movies?.minPremiereDate || ''}" style="width: 100%;">
|
||||||
|
<div class="fieldDescription" style="margin-top: 0.25em;">Optional. Leave empty for default (30 days ago).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em;">
|
||||||
|
<div class="listItemContent">
|
||||||
|
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Max Premiere Date</div>
|
||||||
|
<input type="date" id="homeScreen_recentlyReleased_movies_maxPremiereDate" class="fld emby-input" value="${recentlyReleased.movies?.maxPremiereDate || ''}" style="width: 100%;">
|
||||||
|
<div class="fieldDescription" style="margin-top: 0.25em;">Optional. Leave empty for default (today).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
@@ -1684,8 +1699,23 @@
|
|||||||
<div style="margin-top: 0.75em; margin-bottom: 0.75em;">
|
<div style="margin-top: 0.75em; margin-bottom: 0.75em;">
|
||||||
${buildJellyfinCheckbox('homeScreen_recentlyReleased_episodes_enabled', (recentlyReleased.episodes || {}).enabled !== false, 'Enabled')}
|
${buildJellyfinCheckbox('homeScreen_recentlyReleased_episodes_enabled', (recentlyReleased.episodes || {}).enabled !== false, 'Enabled')}
|
||||||
</div>
|
</div>
|
||||||
<div class="listItemContent" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 0.75em;">
|
<div class="listItemContent" style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75em;">
|
||||||
${createSectionConfiguration('homeScreen_recentlyReleased_episodes', recentlyReleased.episodes || {}, { includeName: true, defaultName: 'Recently Aired Episodes', includeSortOrder: false, includeOrder: true, includeIsPlayed: true })}
|
${createSectionConfiguration('homeScreen_recentlyReleased_episodes', recentlyReleased.episodes || {}, { includeName: true, defaultName: 'Recently Aired Episodes', includeSortOrder: false, includeOrder: true, includeIsPlayed: true })}
|
||||||
|
|
||||||
|
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em;">
|
||||||
|
<div class="listItemContent">
|
||||||
|
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Min Premiere Date</div>
|
||||||
|
<input type="date" id="homeScreen_recentlyReleased_episodes_minPremiereDate" class="fld emby-input" value="${recentlyReleased.episodes?.minPremiereDate || ''}" style="width: 100%;">
|
||||||
|
<div class="fieldDescription" style="margin-top: 0.25em;">Optional. Leave empty for default (7 days ago).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em;">
|
||||||
|
<div class="listItemContent">
|
||||||
|
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Max Premiere Date</div>
|
||||||
|
<input type="date" id="homeScreen_recentlyReleased_episodes_maxPremiereDate" class="fld emby-input" value="${recentlyReleased.episodes?.maxPremiereDate || ''}" style="width: 100%;">
|
||||||
|
<div class="fieldDescription" style="margin-top: 0.25em;">Optional. Leave empty for default (today).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
@@ -1783,6 +1813,13 @@
|
|||||||
</summary>
|
</summary>
|
||||||
<div style="padding: 0.75em 0 0 0;">
|
<div style="padding: 0.75em 0 0 0;">
|
||||||
<div id="homeScreen_seasonal_container" style="${seasonal.enabled === false ? 'display: none;' : ''}">
|
<div id="homeScreen_seasonal_container" style="${seasonal.enabled === false ? 'display: none;' : ''}">
|
||||||
|
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em; margin-bottom: 1em;">
|
||||||
|
<div class="listItemContent">
|
||||||
|
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Enable Seasonal Animations</div>
|
||||||
|
<div class="listItemBodyText secondary" style="margin-bottom: 0.75em; font-size: 0.9em;">Enable falling snow/leaves animations for seasonal themes</div>
|
||||||
|
${buildJellyfinCheckbox('homeScreen_seasonal_enableSeasonalAnimations', seasonal.enableSeasonalAnimations !== false, 'Enabled')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="homeScreen_seasonal_seasons_list" style="margin-bottom: 1em;">
|
<div id="homeScreen_seasonal_seasons_list" style="margin-bottom: 1em;">
|
||||||
${seasonalSectionsHtml}
|
${seasonalSectionsHtml}
|
||||||
</div>
|
</div>
|
||||||
@@ -4955,14 +4992,18 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
|||||||
enabled: document.getElementById('homeScreen_recentlyReleased_enabled')?.checked !== false,
|
enabled: document.getElementById('homeScreen_recentlyReleased_enabled')?.checked !== false,
|
||||||
movies: {
|
movies: {
|
||||||
enabled: document.getElementById('homeScreen_recentlyReleased_movies_enabled')?.checked !== false,
|
enabled: document.getElementById('homeScreen_recentlyReleased_movies_enabled')?.checked !== false,
|
||||||
...getSectionConfig('homeScreen_recentlyReleased_movies', { includeName: true, defaultName: 'Recently Released Movies', includeSortOrder: false, includeOrder: true })
|
...getSectionConfig('homeScreen_recentlyReleased_movies', { includeName: true, defaultName: 'Recently Released Movies', includeSortOrder: false, includeOrder: true }),
|
||||||
},
|
minPremiereDate: document.getElementById('homeScreen_recentlyReleased_movies_minPremiereDate')?.value || '',
|
||||||
episodes: {
|
maxPremiereDate: document.getElementById('homeScreen_recentlyReleased_movies_maxPremiereDate')?.value || ''
|
||||||
enabled: document.getElementById('homeScreen_recentlyReleased_episodes_enabled')?.checked !== false,
|
},
|
||||||
...getSectionConfig('homeScreen_recentlyReleased_episodes', { includeName: true, defaultName: 'Recently Aired Episodes', includeSortOrder: false, includeOrder: true })
|
episodes: {
|
||||||
}
|
enabled: document.getElementById('homeScreen_recentlyReleased_episodes_enabled')?.checked !== false,
|
||||||
},
|
minPremiereDate: document.getElementById('homeScreen_recentlyReleased_episodes_minPremiereDate')?.value || '',
|
||||||
trending: {
|
maxPremiereDate: document.getElementById('homeScreen_recentlyReleased_episodes_maxPremiereDate')?.value || '',
|
||||||
|
...getSectionConfig('homeScreen_recentlyReleased_episodes', { includeName: true, defaultName: 'Recently Aired Episodes', includeSortOrder: false, includeOrder: true })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trending: {
|
||||||
enabled: document.getElementById('homeScreen_trending_enabled')?.checked === true,
|
enabled: document.getElementById('homeScreen_trending_enabled')?.checked === true,
|
||||||
...getSectionConfig('homeScreen_trending', { includeName: true, defaultName: 'Trending' })
|
...getSectionConfig('homeScreen_trending', { includeName: true, defaultName: 'Trending' })
|
||||||
},
|
},
|
||||||
@@ -4989,6 +5030,7 @@ window.KefinTweaksConfig = ${JSON.stringify(config, null, 2)};`;
|
|||||||
},
|
},
|
||||||
seasonal: {
|
seasonal: {
|
||||||
enabled: document.getElementById('homeScreen_seasonal_enabled')?.checked !== false,
|
enabled: document.getElementById('homeScreen_seasonal_enabled')?.checked !== false,
|
||||||
|
enableSeasonalAnimations: document.getElementById('homeScreen_seasonal_enableSeasonalAnimations')?.checked !== false,
|
||||||
defaultItemLimit: 16,
|
defaultItemLimit: 16,
|
||||||
defaultSortOrder: 'Random',
|
defaultSortOrder: 'Random',
|
||||||
defaultCardFormat: 'Poster',
|
defaultCardFormat: 'Poster',
|
||||||
|
|||||||
@@ -70,6 +70,29 @@
|
|||||||
background-size: auto 102%;
|
background-size: auto 102%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Christmas theme */
|
||||||
|
.home-screen.christmas-theme #reactRoot:has(.libraryPage:not(.hide) .pageTabContent.is-active[data-index="0"])>.backgroundContainer::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0%;
|
||||||
|
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('../pages/images/christmas-2.jpg');
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flurry-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-screen.christmas-theme .flurry-container {
|
||||||
|
display: block;
|
||||||
|
position: fixed !important;
|
||||||
|
z-index: 9999999999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.home-screen h2.sectionTitle {
|
.home-screen h2.sectionTitle {
|
||||||
margin-bottom: .35em;
|
margin-bottom: .35em;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
|||||||
+128
-4
@@ -138,6 +138,11 @@
|
|||||||
const currentView = window.KefinTweaksUtils?.getCurrentView();
|
const currentView = window.KefinTweaksUtils?.getCurrentView();
|
||||||
const isHomePage = currentView === 'home' || currentView === 'home.html';
|
const isHomePage = currentView === 'home' || currentView === 'home.html';
|
||||||
|
|
||||||
|
// Get seasonal animation setting
|
||||||
|
const homeScreenConfig = window.KefinTweaksConfig?.homeScreen || {};
|
||||||
|
const seasonalConfig = homeScreenConfig.seasonal || {};
|
||||||
|
const enableSeasonalAnimations = seasonalConfig.enableSeasonalAnimations !== false;
|
||||||
|
|
||||||
// Manage home-screen class
|
// Manage home-screen class
|
||||||
if (isHomePage && body) {
|
if (isHomePage && body) {
|
||||||
body.classList.add('home-screen');
|
body.classList.add('home-screen');
|
||||||
@@ -156,6 +161,30 @@
|
|||||||
body.classList.add('halloween-theme');
|
body.classList.add('halloween-theme');
|
||||||
} else if (isChristmasPeriod()) {
|
} else if (isChristmasPeriod()) {
|
||||||
body.classList.add('christmas-theme');
|
body.classList.add('christmas-theme');
|
||||||
|
|
||||||
|
if (enableSeasonalAnimations) {
|
||||||
|
// Import the Flurry module and leverage it from jQuery. /scripts/third party/flurry.js by appending it to the head
|
||||||
|
const flurryScriptSrc = (window.KefinTweaksConfig.kefinTweaksRoot || '') + 'scripts/third party/jquery.flurry.min.js';
|
||||||
|
|
||||||
|
// Function to run flurry safely
|
||||||
|
const startFlurry = () => {
|
||||||
|
if (window.jQuery && typeof window.jQuery.fn.flurry === 'function') {
|
||||||
|
window.jQuery("body").flurry();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if script is already present
|
||||||
|
if (!document.querySelector(`script[src="${flurryScriptSrc}"]`)) {
|
||||||
|
const flurryScript = document.createElement('script');
|
||||||
|
flurryScript.src = flurryScriptSrc;
|
||||||
|
flurryScript.onload = startFlurry; // Run after load
|
||||||
|
document.head.appendChild(flurryScript);
|
||||||
|
} else {
|
||||||
|
// Script loaded, try running directly
|
||||||
|
startFlurry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (isValentinesPeriod()) {
|
} else if (isValentinesPeriod()) {
|
||||||
body.classList.add('valentines-theme');
|
body.classList.add('valentines-theme');
|
||||||
} else if (isNewYearsPeriod()) {
|
} else if (isNewYearsPeriod()) {
|
||||||
@@ -587,6 +616,90 @@
|
|||||||
WARN('KefinTweaksUtils.saveConfigToJavaScriptInjector not available, config migration not persisted');
|
WARN('KefinTweaksUtils.saveConfigToJavaScriptInjector not available, config migration not persisted');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- NEW UPDATE LOGIC ---
|
||||||
|
// Runs even if no major migration was needed, to catch specific updates
|
||||||
|
|
||||||
|
// 1. Ensure Recently Released date fields exist
|
||||||
|
let configChanged = false;
|
||||||
|
// Use the current config (which might have been just migrated)
|
||||||
|
const currentConfig = window.KefinTweaksConfig.homeScreen;
|
||||||
|
|
||||||
|
if (currentConfig.recentlyReleased) {
|
||||||
|
if (currentConfig.recentlyReleased.movies && currentConfig.recentlyReleased.movies.minPremiereDate === undefined) {
|
||||||
|
currentConfig.recentlyReleased.movies.minPremiereDate = "";
|
||||||
|
currentConfig.recentlyReleased.movies.maxPremiereDate = "";
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
if (currentConfig.recentlyReleased.episodes && currentConfig.recentlyReleased.episodes.minPremiereDate === undefined) {
|
||||||
|
currentConfig.recentlyReleased.episodes.minPremiereDate = "";
|
||||||
|
currentConfig.recentlyReleased.episodes.maxPremiereDate = "";
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Update Christmas Seasonal Sections
|
||||||
|
if (currentConfig.seasonal && Array.isArray(currentConfig.seasonal.seasons)) {
|
||||||
|
const christmasSeason = currentConfig.seasonal.seasons.find(s => s.id === 'christmas');
|
||||||
|
if (christmasSeason && Array.isArray(christmasSeason.sections)) {
|
||||||
|
|
||||||
|
// Check for old "Christmas Movies" (genre) and update to new "Christmas Movies" (tag)
|
||||||
|
const movieSectionIndex = christmasSeason.sections.findIndex(s => s.id === 'christmas-genre');
|
||||||
|
if (movieSectionIndex !== -1) {
|
||||||
|
christmasSeason.sections[movieSectionIndex] = {
|
||||||
|
"id": "seasonal-1-section-0",
|
||||||
|
"enabled": true,
|
||||||
|
"name": "Christmas Movies",
|
||||||
|
"type": "Tag",
|
||||||
|
"source": "christmas",
|
||||||
|
"itemLimit": 16,
|
||||||
|
"sortOrder": "Random",
|
||||||
|
"sortOrderDirection": "Ascending",
|
||||||
|
"cardFormat": "Poster",
|
||||||
|
"order": 50,
|
||||||
|
"spotlight": true,
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"searchTerm": "",
|
||||||
|
"includeItemTypes": ["Movie"],
|
||||||
|
"additionalQueryOptions": []
|
||||||
|
};
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for old "Family Movies" (genre) and update to new "Christmas Episodes" (parent/search)
|
||||||
|
const familySectionIndex = christmasSeason.sections.findIndex(s => s.id === 'christmas-family');
|
||||||
|
if (familySectionIndex !== -1) {
|
||||||
|
christmasSeason.sections[familySectionIndex] = {
|
||||||
|
"id": "seasonal-1-section-1",
|
||||||
|
"enabled": true,
|
||||||
|
"name": "Christmas Episodes",
|
||||||
|
"type": "Parent",
|
||||||
|
"source": "",
|
||||||
|
"itemLimit": 16,
|
||||||
|
"sortOrder": "Random",
|
||||||
|
"sortOrderDirection": "Ascending",
|
||||||
|
"cardFormat": "Thumb",
|
||||||
|
"order": 51,
|
||||||
|
"spotlight": false,
|
||||||
|
"discoveryEnabled": false,
|
||||||
|
"searchTerm": "christmas",
|
||||||
|
"includeItemTypes": ["Episode"],
|
||||||
|
"additionalQueryOptions": []
|
||||||
|
};
|
||||||
|
configChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configChanged && !needsMigration) {
|
||||||
|
LOG('Applied specific configuration updates (recently released dates / christmas sections)');
|
||||||
|
// Save updated config back to JS Injector plugin
|
||||||
|
if (window.KefinTweaksUtils && window.KefinTweaksUtils.saveConfigToJavaScriptInjector) {
|
||||||
|
window.KefinTweaksUtils.saveConfigToJavaScriptInjector().catch(err => {
|
||||||
|
ERR('Failed to save updated config to JS Injector:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run migration before reading config
|
// Run migration before reading config
|
||||||
@@ -644,6 +757,7 @@
|
|||||||
// Seasonal configuration
|
// Seasonal configuration
|
||||||
const seasonalConfig = homeScreenConfig.seasonal || {};
|
const seasonalConfig = homeScreenConfig.seasonal || {};
|
||||||
const enableSeasonal = seasonalConfig.enabled !== false;
|
const enableSeasonal = seasonalConfig.enabled !== false;
|
||||||
|
const enableSeasonalAnimations = seasonalConfig.enableSeasonalAnimations !== false;
|
||||||
const SEASONAL_ITEM_LIMIT = seasonalConfig.defaultItemLimit || 16;
|
const SEASONAL_ITEM_LIMIT = seasonalConfig.defaultItemLimit || 16;
|
||||||
|
|
||||||
// Discovery sections configuration
|
// Discovery sections configuration
|
||||||
@@ -2445,8 +2559,8 @@
|
|||||||
Limit: 16,
|
Limit: 16,
|
||||||
Fields: 'PremiereDate',
|
Fields: 'PremiereDate',
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
MinPremiereDate: monthAgo,
|
MinPremiereDate: newMoviesConfig.minPremiereDate || monthAgo,
|
||||||
MaxPremiereDate: today
|
MaxPremiereDate: newMoviesConfig.maxPremiereDate || today
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPlayedFilter = getIsPlayedFilter(newMoviesConfig);
|
const isPlayedFilter = getIsPlayedFilter(newMoviesConfig);
|
||||||
@@ -2454,6 +2568,11 @@
|
|||||||
options.Filters = isPlayedFilter;
|
options.Filters = isPlayedFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add UserData to Fields if IsPlayed filter is active or generally useful
|
||||||
|
if (!options.Fields.includes('UserData')) {
|
||||||
|
options.Fields += ',UserData';
|
||||||
|
}
|
||||||
|
|
||||||
const data = await window.apiHelper.getItems(options, true);
|
const data = await window.apiHelper.getItems(options, true);
|
||||||
return data.Items || [];
|
return data.Items || [];
|
||||||
}
|
}
|
||||||
@@ -2478,8 +2597,8 @@
|
|||||||
Recursive: true,
|
Recursive: true,
|
||||||
SortBy: 'PremiereDate',
|
SortBy: 'PremiereDate',
|
||||||
SortOrder: 'Descending',
|
SortOrder: 'Descending',
|
||||||
MinPremiereDate: minDate,
|
MinPremiereDate: newEpisodesConfig.minPremiereDate || minDate,
|
||||||
MaxPremiereDate: maxDate,
|
MaxPremiereDate: newEpisodesConfig.maxPremiereDate || maxDate,
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
Fields: 'PremiereDate,SeriesName,ParentIndexNumber,IndexNumber,ProviderIds,Path'
|
Fields: 'PremiereDate,SeriesName,ParentIndexNumber,IndexNumber,ProviderIds,Path'
|
||||||
}
|
}
|
||||||
@@ -2489,6 +2608,11 @@
|
|||||||
options.Filters = isPlayedFilter;
|
options.Filters = isPlayedFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add UserData to Fields if IsPlayed filter is active or generally useful
|
||||||
|
if (!options.Fields.includes('UserData')) {
|
||||||
|
options.Fields += ',UserData';
|
||||||
|
}
|
||||||
|
|
||||||
const data = await window.apiHelper.getItems(options, true);
|
const data = await window.apiHelper.getItems(options, true);
|
||||||
const episodes = data.Items || [];
|
const episodes = data.Items || [];
|
||||||
|
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Flurry jQuery Plugin
|
||||||
|
*
|
||||||
|
* Flurry is an easy-to-use animated snow plugin for jQuery. It takes advantage
|
||||||
|
* of CSS transforms, CSS transitions and requestAnimationFrame to provide
|
||||||
|
* smooth animation for modern browsers. Props to Jonathan Nicol @f6design
|
||||||
|
* for boilerplate code
|
||||||
|
* (see http://jonathannicol.com/blog/2012/05/06/a-jquery-plugin-boilerplate/)
|
||||||
|
*
|
||||||
|
* @link https://github.com/joshmcrty/Flurry
|
||||||
|
* @version 1.2.0
|
||||||
|
* @author Josh McCarty <josh@joshmccarty.com>
|
||||||
|
* @copyright 2016 Josh McCarty
|
||||||
|
* @license https://github.com/joshmcrty/Flurry/blob/master/LICENSE GPLv2
|
||||||
|
*/
|
||||||
|
!function(n,e){!function(){for(var n=0,e=["ms","moz","webkit","o"],a=0;a<e.length&&!window.requestAnimationFrame;++a)window.requestAnimationFrame=window[e[a]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[a]+"CancelAnimationFrame"]||window[e[a]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e,a){var t=(new Date).getTime(),i=Math.max(0,16-Math.abs(t-n)),r=window.setTimeout(function(){e(t+i)},i);return n=t+i,r}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(n){clearTimeout(n)})}();var a="flurry";function t(n,e){return Math.floor(Math.random()*(e-n+1)+n)}function i(a,i,r){var o=1===a.character.length?Array.isArray(a.character)?a.character[0]:a.character:Array.isArray(a.character)?a.character[t(0,a.character.length-1)]:Array.from(a.character)[t(0,a.character.length-1)];"string"==typeof a.character&&console&&console.warn&&console.warn("You are using a string for the Flurry character options. Please use an array of string(s) instead.");var s=t(-Math.abs(a.wind),r+Math.abs(a.wind)),c=s+t(a.wind-a.windVariance,a.wind+a.windVariance),l=t(a.small,a.large),u=a.speed/((t(1.2*l,.8*l)-a.small)/(a.large-a.small)+.5),m=a.height-l,w=t(a.rotation-a.rotationVariance,a.rotation+a.rotationVariance),d=Array.isArray(a.color)?a.color[Math.floor(Math.random()*a.color.length)]:a.color,h={transform:"translateX("+c+"px) translateY("+m+"px) rotateZ("+w+"deg)",opacity:0},f=n("<span></span>");f.html(o).css({color:a.blur&&l<(a.large+a.small)/2?"transparent":d,"text-shadow":a.blur&&l<(a.large+a.small)/2?"0 0 1px "+d:"none",display:"inline-block","line-height":1,margin:0,padding:"2px","pointer-events":"none","font-size":l+"px",opacity:a.startTransparency,position:"absolute",top:"-"+1.2*a.large+"px",transform:"translateX("+s+"px) translateY(0px) rotateZ("+a.startRotation+"deg)",transition:"transform "+u/1e3+"s linear, opacity "+u/1e3+"s "+a.opacityEasing,"z-index":a.zIndex}),a.onFlake!==e&&a.onFlake.call(f[0]),f.appendTo(i),!function(){var n=(document.body||document.documentElement).style;return n.transition!==e||n.WebkitTransition!==e||n.MozTransition!==e||n.MsTransition!==e||n.OTransition!==e}?f.animate(h,u,"linear",function(){n(this).remove()}):(f.on("transitionend.flurry",function(e){n(e.target).remove()}),window.requestAnimationFrame(function(){f.css(h)}))}function r(t,r){var o=this,s=t,c=n(t);function l(n){r[n]!==e&&r[n].call(s)}return r=n.extend({height:c.height()>200?200:c.height(),useRelative:!c.is("body")},n.fn[a].defaults,r),n.each(r,function(n,e){parseInt(e)&&(r[n]=parseInt(e))}),function(){!0===r.useRelative&&"static"===c.css("position")&&c.css({position:"relative"});var e,a,t,s,u=n(document.createElement("div")).addClass("flurry-container").css({margin:0,padding:0,position:"absolute",top:0,right:0,left:0,height:r.height,overflow:r.overflow,"pointer-events":"none"}).prependTo(c),m=u.width();n(window).resize(function(){m=u.width()}),o.flakeInterval=(e=function(){i(r,u,m)},a=r.frequency,t=(new Date).getTime(),(s=new Object).value=window.requestAnimationFrame(function n(){(new Date).getTime()-t>=a&&(e.call(),t=(new Date).getTime()),s.value=window.requestAnimationFrame(n)}),s),l("onInit")}(),{option:function(n,e){if(!e)return r[n];r[n]=parseInt(e)||e},destroy:function(){c.each(function(){var e,t=n(this);e=o.flakeInterval,window.cancelAnimationFrame?window.cancelAnimationFrame(e.value):window.webkitCancelAnimationFrame?window.webkitCancelAnimationFrame(e.value):window.webkitCancelRequestAnimationFrame?window.webkitCancelRequestAnimationFrame(e.value):window.mozCancelRequestAnimationFrame?window.mozCancelRequestAnimationFrame(e.value):window.oCancelRequestAnimationFrame?window.oCancelRequestAnimationFrame(e.value):window.msCancelRequestAnimationFrame?window.msCancelRequestAnimationFrame(e.value):clearInterval(e),t.find(".flurry-container").remove(),l("onDestroy"),t.removeData("plugin_"+a)})}}}n.fn[a]=function(t){if("string"==typeof arguments[0]){var i,o=arguments[0],s=Array.prototype.slice.call(arguments,1);return this.each(function(){if(!n.data(this,"plugin_"+a)||"function"!=typeof n.data(this,"plugin_"+a)[o])throw new Error("Method "+o+" does not exist on jQuery."+a);i=n.data(this,"plugin_"+a)[o].apply(this,s)}),i!==e?i:this}if("object"==typeof t||!t)return this.each(function(){n.data(this,"plugin_"+a)||n.data(this,"plugin_"+a,new r(this,t))})},n.fn[a].defaults={onInit:e,onDestroy:e,onFlake:e,character:["❄"],color:"white",frequency:100,speed:3e3,small:8,large:28,wind:40,windVariance:20,rotation:90,rotationVariance:180,startRotation:0,startOpacity:1,endOpacity:0,opacityEasing:"cubic-bezier(1,.3,.6,.74)",blur:!1,overflow:"hidden",zIndex:9999}}(jQuery);
|
||||||
Reference in new issue
Block a user