mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Recommendation home page (#2571)
This commit is contained in:
@@ -64,8 +64,10 @@
|
|||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-router-hash-link": "^2.3.1",
|
"react-router-hash-link": "^2.3.1",
|
||||||
"react-select": "^4.0.2",
|
"react-select": "^4.0.2",
|
||||||
|
"react-slick": "^0.29.0",
|
||||||
"remark-gfm": "^1.0.0",
|
"remark-gfm": "^1.0.0",
|
||||||
"sass": "^1.32.5",
|
"sass": "^1.32.5",
|
||||||
|
"slick-carousel": "^1.8.1",
|
||||||
"string.prototype.replaceall": "^1.0.4",
|
"string.prototype.replaceall": "^1.0.4",
|
||||||
"subscriptions-transport-ws": "^0.9.18",
|
"subscriptions-transport-ws": "^0.9.18",
|
||||||
"thehandy": "^1.0.3",
|
"thehandy": "^1.0.3",
|
||||||
@@ -99,6 +101,7 @@
|
|||||||
"@types/react-router-bootstrap": "^0.24.5",
|
"@types/react-router-bootstrap": "^0.24.5",
|
||||||
"@types/react-router-dom": "5.1.7",
|
"@types/react-router-dom": "5.1.7",
|
||||||
"@types/react-router-hash-link": "^1.2.1",
|
"@types/react-router-hash-link": "^1.2.1",
|
||||||
|
"@types/react-slick": "^0.23.8",
|
||||||
"@types/video.js": "^7.3.28",
|
"@types/video.js": "^7.3.28",
|
||||||
"@types/videojs-seek-buttons": "^2.1.0",
|
"@types/videojs-seek-buttons": "^2.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import Galleries from "./components/Galleries/Galleries";
|
|||||||
import { MainNavbar } from "./components/MainNavbar";
|
import { MainNavbar } from "./components/MainNavbar";
|
||||||
import { PageNotFound } from "./components/PageNotFound";
|
import { PageNotFound } from "./components/PageNotFound";
|
||||||
import Performers from "./components/Performers/Performers";
|
import Performers from "./components/Performers/Performers";
|
||||||
|
import Recommendations from "./components/Recommendations/Recommendations";
|
||||||
import Scenes from "./components/Scenes/Scenes";
|
import Scenes from "./components/Scenes/Scenes";
|
||||||
import { Settings } from "./components/Settings/Settings";
|
import { Settings } from "./components/Settings/Settings";
|
||||||
import { Stats } from "./components/Stats";
|
import { Stats } from "./components/Stats";
|
||||||
@@ -117,7 +118,7 @@ export const App: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" component={Stats} />
|
<Route exact path="/" component={Recommendations} />
|
||||||
<Route path="/scenes" component={Scenes} />
|
<Route path="/scenes" component={Scenes} />
|
||||||
<Route path="/images" component={Images} />
|
<Route path="/images" component={Images} />
|
||||||
<Route path="/galleries" component={Galleries} />
|
<Route path="/galleries" component={Galleries} />
|
||||||
@@ -125,6 +126,7 @@ export const App: React.FC = () => {
|
|||||||
<Route path="/tags" component={Tags} />
|
<Route path="/tags" component={Tags} />
|
||||||
<Route path="/studios" component={Studios} />
|
<Route path="/studios" component={Studios} />
|
||||||
<Route path="/movies" component={Movies} />
|
<Route path="/movies" component={Movies} />
|
||||||
|
<Route path="/stats" component={Stats} />
|
||||||
<Route path="/settings" component={Settings} />
|
<Route path="/settings" component={Settings} />
|
||||||
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
<Route path="/sceneFilenameParser" component={SceneFilenameParser} />
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
### ✨ New Features
|
### ✨ New Features
|
||||||
|
* Added recommendations to home page. ([#2571](https://github.com/stashapp/stash/pull/2571))
|
||||||
* Add support for VTT and SRT captions for scenes. ([#2462](https://github.com/stashapp/stash/pull/2462))
|
* Add support for VTT and SRT captions for scenes. ([#2462](https://github.com/stashapp/stash/pull/2462))
|
||||||
* Added option to require a number of scroll attempts before navigating to next/previous image in Lightbox. ([#2544](https://github.com/stashapp/stash/pull/2544))
|
* Added option to require a number of scroll attempts before navigating to next/previous image in Lightbox. ([#2544](https://github.com/stashapp/stash/pull/2544))
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ const messages = defineMessages({
|
|||||||
id: "donate",
|
id: "donate",
|
||||||
defaultMessage: "Donate",
|
defaultMessage: "Donate",
|
||||||
},
|
},
|
||||||
|
statistics: {
|
||||||
|
id: "statistics",
|
||||||
|
defaultMessage: "Statistics",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const allMenuItems: IMenuItem[] = [
|
const allMenuItems: IMenuItem[] = [
|
||||||
@@ -259,6 +263,19 @@ export const MainNavbar: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Nav.Link>
|
</Nav.Link>
|
||||||
|
<NavLink
|
||||||
|
className="nav-utility"
|
||||||
|
exact
|
||||||
|
to="/stats"
|
||||||
|
onClick={handleDismiss}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="minimal d-flex align-items-center h-100"
|
||||||
|
title={intl.formatMessage({ id: "statistics" })}
|
||||||
|
>
|
||||||
|
<Icon icon="chart-bar" />
|
||||||
|
</Button>
|
||||||
|
</NavLink>
|
||||||
<NavLink
|
<NavLink
|
||||||
className="nav-utility"
|
className="nav-utility"
|
||||||
exact
|
exact
|
||||||
|
|||||||
263
ui/v2.5/src/components/Recommendations/Recommendations.tsx
Normal file
263
ui/v2.5/src/components/Recommendations/Recommendations.tsx
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
import * as GQL from "src/core/generated-graphql";
|
||||||
|
import { defineMessages, useIntl } from "react-intl";
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
useFindScenes,
|
||||||
|
useFindMovies,
|
||||||
|
useFindStudios,
|
||||||
|
useFindGalleries,
|
||||||
|
useFindPerformers,
|
||||||
|
} from "src/core/StashService";
|
||||||
|
import { SceneCard } from "src/components/Scenes/SceneCard";
|
||||||
|
import { StudioCard } from "src/components/Studios/StudioCard";
|
||||||
|
import { MovieCard } from "src/components/Movies/MovieCard";
|
||||||
|
import { PerformerCard } from "src/components/Performers/PerformerCard";
|
||||||
|
import { GalleryCard } from "src/components/Galleries/GalleryCard";
|
||||||
|
import { SceneQueue } from "src/models/sceneQueue";
|
||||||
|
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
|
import Slider from "react-slick";
|
||||||
|
|
||||||
|
const Recommendations: React.FC = () => {
|
||||||
|
function isTouchEnabled() {
|
||||||
|
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTouch = isTouchEnabled();
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
const itemsPerPage = 25;
|
||||||
|
const scenefilter = new ListFilterModel(GQL.FilterMode.Scenes);
|
||||||
|
scenefilter.sortBy = "date";
|
||||||
|
scenefilter.sortDirection = GQL.SortDirectionEnum.Desc;
|
||||||
|
scenefilter.itemsPerPage = itemsPerPage;
|
||||||
|
const sceneResult = useFindScenes(scenefilter);
|
||||||
|
const hasScenes =
|
||||||
|
sceneResult.data &&
|
||||||
|
sceneResult.data.findScenes &&
|
||||||
|
sceneResult.data.findScenes.count > 0;
|
||||||
|
|
||||||
|
const studiofilter = new ListFilterModel(GQL.FilterMode.Studios);
|
||||||
|
studiofilter.sortBy = "scenes_count";
|
||||||
|
studiofilter.sortDirection = GQL.SortDirectionEnum.Desc;
|
||||||
|
studiofilter.itemsPerPage = itemsPerPage;
|
||||||
|
const studioResult = useFindStudios(studiofilter);
|
||||||
|
const hasStudios =
|
||||||
|
studioResult.data &&
|
||||||
|
studioResult.data.findStudios &&
|
||||||
|
studioResult.data.findStudios.count > 0;
|
||||||
|
|
||||||
|
const moviefilter = new ListFilterModel(GQL.FilterMode.Movies);
|
||||||
|
moviefilter.sortBy = "date";
|
||||||
|
moviefilter.sortDirection = GQL.SortDirectionEnum.Desc;
|
||||||
|
moviefilter.itemsPerPage = itemsPerPage;
|
||||||
|
const movieResult = useFindMovies(moviefilter);
|
||||||
|
const hasMovies =
|
||||||
|
movieResult.data &&
|
||||||
|
movieResult.data.findMovies &&
|
||||||
|
movieResult.data.findMovies.count > 0;
|
||||||
|
|
||||||
|
const performerfilter = new ListFilterModel(GQL.FilterMode.Performers);
|
||||||
|
performerfilter.sortBy = "created_at";
|
||||||
|
performerfilter.sortDirection = GQL.SortDirectionEnum.Desc;
|
||||||
|
performerfilter.itemsPerPage = itemsPerPage;
|
||||||
|
const performerResult = useFindPerformers(performerfilter);
|
||||||
|
const hasPerformers =
|
||||||
|
performerResult.data &&
|
||||||
|
performerResult.data.findPerformers &&
|
||||||
|
performerResult.data.findPerformers.count > 0;
|
||||||
|
|
||||||
|
const galleryfilter = new ListFilterModel(GQL.FilterMode.Galleries);
|
||||||
|
galleryfilter.sortBy = "date";
|
||||||
|
galleryfilter.sortDirection = GQL.SortDirectionEnum.Desc;
|
||||||
|
galleryfilter.itemsPerPage = itemsPerPage;
|
||||||
|
const galleryResult = useFindGalleries(galleryfilter);
|
||||||
|
const hasGalleries =
|
||||||
|
galleryResult.data &&
|
||||||
|
galleryResult.data.findGalleries &&
|
||||||
|
galleryResult.data.findGalleries.count > 0;
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
emptyServer: {
|
||||||
|
id: "empty_server",
|
||||||
|
defaultMessage:
|
||||||
|
"Add some scenes to your server to view recommendations on this page.",
|
||||||
|
},
|
||||||
|
latestScenes: {
|
||||||
|
id: "latest_scenes",
|
||||||
|
defaultMessage: "Latest Scenes",
|
||||||
|
},
|
||||||
|
mostActiveStudios: {
|
||||||
|
id: "most_active_studios",
|
||||||
|
defaultMessage: "Most Active Studios",
|
||||||
|
},
|
||||||
|
latestMovies: {
|
||||||
|
id: "latest_movies",
|
||||||
|
defaultMessage: "Latest Movies",
|
||||||
|
},
|
||||||
|
latestPerformers: {
|
||||||
|
id: "latest_performers",
|
||||||
|
defaultMessage: "Latest Performers",
|
||||||
|
},
|
||||||
|
latestGalleries: {
|
||||||
|
id: "latest_galleries",
|
||||||
|
defaultMessage: "Latest Galleries",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var settings = {
|
||||||
|
dots: !isTouch,
|
||||||
|
arrows: !isTouch,
|
||||||
|
infinite: !isTouch,
|
||||||
|
speed: 300,
|
||||||
|
variableWidth: true,
|
||||||
|
swipeToSlide: true,
|
||||||
|
slidesToShow: 5,
|
||||||
|
slidesToScroll: !isTouch ? 5 : 1,
|
||||||
|
responsive: [
|
||||||
|
{
|
||||||
|
breakpoint: 1909,
|
||||||
|
settings: {
|
||||||
|
slidesToShow: 4,
|
||||||
|
slidesToScroll: !isTouch ? 4 : 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
breakpoint: 1542,
|
||||||
|
settings: {
|
||||||
|
slidesToShow: 3,
|
||||||
|
slidesToScroll: !isTouch ? 3 : 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
breakpoint: 1170,
|
||||||
|
settings: {
|
||||||
|
slidesToShow: 2,
|
||||||
|
slidesToScroll: !isTouch ? 2 : 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
breakpoint: 801,
|
||||||
|
settings: {
|
||||||
|
slidesToShow: 1,
|
||||||
|
slidesToScroll: 1,
|
||||||
|
dots: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const queue = SceneQueue.fromListFilterModel(scenefilter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="recommendations-container">
|
||||||
|
{!hasScenes &&
|
||||||
|
!hasStudios &&
|
||||||
|
!hasMovies &&
|
||||||
|
!hasPerformers &&
|
||||||
|
!hasGalleries ? (
|
||||||
|
<div className="no-recommendations">
|
||||||
|
{intl.formatMessage(messages.emptyServer)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{hasScenes && (
|
||||||
|
<div className="recommendation-row">
|
||||||
|
<div className="recommendation-row-head">
|
||||||
|
<div>
|
||||||
|
<h2>{intl.formatMessage(messages.latestScenes)}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/scenes?sortby=date&sortdir=desc">View all</a>
|
||||||
|
</div>
|
||||||
|
<Slider {...settings}>
|
||||||
|
{sceneResult.data?.findScenes.scenes.map((scene, index) => (
|
||||||
|
<SceneCard
|
||||||
|
key={scene.id}
|
||||||
|
scene={scene}
|
||||||
|
queue={queue}
|
||||||
|
index={index}
|
||||||
|
zoomIndex={1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasStudios && (
|
||||||
|
<div className="recommendation-row">
|
||||||
|
<div className="recommendation-row-head">
|
||||||
|
<div>
|
||||||
|
<h2>{intl.formatMessage(messages.mostActiveStudios)}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/studios?sortby=scenes_count&sortdir=desc">View all</a>
|
||||||
|
</div>
|
||||||
|
<Slider {...settings}>
|
||||||
|
{studioResult.data?.findStudios.studios.map((studio) => (
|
||||||
|
<StudioCard
|
||||||
|
key={studio.id}
|
||||||
|
studio={studio}
|
||||||
|
hideParent={true}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasMovies && (
|
||||||
|
<div className="recommendation-row">
|
||||||
|
<div className="recommendation-row-head">
|
||||||
|
<div>
|
||||||
|
<h2>{intl.formatMessage(messages.latestMovies)}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/movies?sortby=date&sortdir=desc">View all</a>
|
||||||
|
</div>
|
||||||
|
<Slider {...settings}>
|
||||||
|
{movieResult.data?.findMovies.movies.map((p) => (
|
||||||
|
<MovieCard key={p.id} movie={p} />
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasPerformers && (
|
||||||
|
<div className="recommendation-row">
|
||||||
|
<div className="recommendation-row-head">
|
||||||
|
<div>
|
||||||
|
<h2>{intl.formatMessage(messages.latestPerformers)}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/performers?sortby=created_at&sortdir=desc">
|
||||||
|
View all
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<Slider {...settings}>
|
||||||
|
{performerResult.data?.findPerformers.performers.map((p) => (
|
||||||
|
<PerformerCard key={p.id} performer={p} />
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasGalleries && (
|
||||||
|
<div className="recommendation-row">
|
||||||
|
<div className="recommendation-row-head">
|
||||||
|
<div>
|
||||||
|
<h2>{intl.formatMessage(messages.latestGalleries)}</h2>
|
||||||
|
</div>
|
||||||
|
<a href="/galleries?sortby=date&sortdir=desc">View all</a>
|
||||||
|
</div>
|
||||||
|
<Slider {...settings}>
|
||||||
|
{galleryResult.data?.findGalleries.galleries.map((gallery) => (
|
||||||
|
<GalleryCard
|
||||||
|
key={gallery.id}
|
||||||
|
gallery={gallery}
|
||||||
|
zoomIndex={1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Slider>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Recommendations;
|
||||||
370
ui/v2.5/src/components/Recommendations/styles.scss
Normal file
370
ui/v2.5/src/components/Recommendations/styles.scss
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
.recommendations-container {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-recommendations {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommendation-row-head {
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 0;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommendation-row-head h2 {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommendation-row-head a {
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommendations-container .studio-card hr,
|
||||||
|
.recommendations-container .movie-card hr,
|
||||||
|
.recommendations-container .gallery-card hr {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slider */
|
||||||
|
.slick-slider {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
-ms-touch-action: pan-y;
|
||||||
|
touch-action: pan-y;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list {
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list.dragging {
|
||||||
|
cursor: pointer;
|
||||||
|
cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slider .slick-track,
|
||||||
|
.slick-slider .slick-list {
|
||||||
|
-moz-transform: translate3d(0, 0, 0);
|
||||||
|
-ms-transform: translate3d(0, 0, 0);
|
||||||
|
-o-transform: translate3d(0, 0, 0);
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-track {
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-track::before,
|
||||||
|
.slick-track::after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-track::after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-loading .slick-track {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide {
|
||||||
|
display: none;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .slick-slide {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide.slick-loading img {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide.dragging img {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-initialized .slick-slide {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-loading .slick-slide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-vertical .slick-slide {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
display: block;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-arrow.slick-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-loading .slick-list {
|
||||||
|
background: #fff url("slick-carousel/slick/ajax-loader.gif") center center
|
||||||
|
no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list .card-check {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid .slick-track {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid .slick-slide {
|
||||||
|
display: flex;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide .card {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-slide .studio-card-image {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.slick-list .scene-card,
|
||||||
|
.slick-list .studio-card,
|
||||||
|
.slick-list .gallery-card {
|
||||||
|
width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list .movie-card {
|
||||||
|
width: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list .performer-card {
|
||||||
|
width: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-list .performer-card-image {
|
||||||
|
height: 24rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icons */
|
||||||
|
@font-face {
|
||||||
|
font-family: slick;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
src: url("slick-carousel/slick/fonts/slick.eot");
|
||||||
|
src: url("slick-carousel/slick/fonts/slick.eot?#iefix")
|
||||||
|
format("embedded-opentype"),
|
||||||
|
url("slick-carousel/slick/fonts/slick.woff") format("woff"),
|
||||||
|
url("slick-carousel/slick/fonts/slick.ttf") format("truetype"),
|
||||||
|
url("slick-carousel/slick/fonts/slick.svg#slick") format("svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Arrows */
|
||||||
|
.slick-prev,
|
||||||
|
.slick-next {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 0;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
-webkit-transform: translate(0, -50%);
|
||||||
|
-ms-transform: translate(0, -50%);
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev:hover,
|
||||||
|
.slick-prev:focus,
|
||||||
|
.slick-next:hover,
|
||||||
|
.slick-next:focus {
|
||||||
|
background: transparent;
|
||||||
|
color: transparent;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev:hover::before,
|
||||||
|
.slick-prev:focus::before,
|
||||||
|
.slick-next:hover::before,
|
||||||
|
.slick-next:focus::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev.slick-disabled::before,
|
||||||
|
.slick-next.slick-disabled::before {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev::before,
|
||||||
|
.slick-next::before {
|
||||||
|
color: white;
|
||||||
|
font-family: slick;
|
||||||
|
font-size: 20px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev {
|
||||||
|
left: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .slick-prev {
|
||||||
|
left: auto;
|
||||||
|
right: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-prev::before {
|
||||||
|
content: "←";
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .slick-prev::before {
|
||||||
|
content: "→";
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-next {
|
||||||
|
right: -25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .slick-next {
|
||||||
|
left: -25px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-next::before {
|
||||||
|
content: "→";
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .slick-next::before {
|
||||||
|
content: "←";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dots */
|
||||||
|
.slick-dotted.slick-slider {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots {
|
||||||
|
bottom: -25px;
|
||||||
|
display: block;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 5px;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li button {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 0;
|
||||||
|
outline: none;
|
||||||
|
padding: 5px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li button:hover,
|
||||||
|
.slick-dots li button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li button:hover::before,
|
||||||
|
.slick-dots li button:focus::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li button::before {
|
||||||
|
color: white;
|
||||||
|
content: "-";
|
||||||
|
font-family: slick;
|
||||||
|
font-size: 50px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
height: 20px;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.25;
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-dots li.slick-active button::before {
|
||||||
|
color: white;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
@import "src/components/List/styles.scss";
|
@import "src/components/List/styles.scss";
|
||||||
@import "src/components/Movies/styles.scss";
|
@import "src/components/Movies/styles.scss";
|
||||||
@import "src/components/Performers/styles.scss";
|
@import "src/components/Performers/styles.scss";
|
||||||
|
@import "src/components/Recommendations/styles.scss";
|
||||||
@import "src/components/Scenes/styles.scss";
|
@import "src/components/Scenes/styles.scss";
|
||||||
@import "src/components/SceneDuplicateChecker/styles.scss";
|
@import "src/components/SceneDuplicateChecker/styles.scss";
|
||||||
@import "src/components/SceneFilenameParser/styles.scss";
|
@import "src/components/SceneFilenameParser/styles.scss";
|
||||||
|
|||||||
@@ -121,6 +121,7 @@
|
|||||||
"birth_year": "Birth Year",
|
"birth_year": "Birth Year",
|
||||||
"birthdate": "Birthdate",
|
"birthdate": "Birthdate",
|
||||||
"bitrate": "Bit Rate",
|
"bitrate": "Bit Rate",
|
||||||
|
"captions": "Captions",
|
||||||
"career_length": "Career Length",
|
"career_length": "Career Length",
|
||||||
"component_tagger": {
|
"component_tagger": {
|
||||||
"config": {
|
"config": {
|
||||||
@@ -705,6 +706,7 @@
|
|||||||
"scale": "Scale",
|
"scale": "Scale",
|
||||||
"warmth": "Warmth"
|
"warmth": "Warmth"
|
||||||
},
|
},
|
||||||
|
"empty_server": "Add some scenes to your server to view recommendations on this page.",
|
||||||
"ethnicity": "Ethnicity",
|
"ethnicity": "Ethnicity",
|
||||||
"existing_value": "existing value",
|
"existing_value": "existing value",
|
||||||
"eye_color": "Eye Colour",
|
"eye_color": "Eye Colour",
|
||||||
@@ -747,8 +749,11 @@
|
|||||||
"instagram": "Instagram",
|
"instagram": "Instagram",
|
||||||
"interactive": "Interactive",
|
"interactive": "Interactive",
|
||||||
"interactive_speed": "Interactive speed",
|
"interactive_speed": "Interactive speed",
|
||||||
"captions": "Captions",
|
|
||||||
"isMissing": "Is Missing",
|
"isMissing": "Is Missing",
|
||||||
|
"latest_galleries": "Latest Galleries",
|
||||||
|
"latest_movies": "Latest Movies",
|
||||||
|
"latest_performers": "Latest Performers",
|
||||||
|
"latest_scenes": "Latest Scenes",
|
||||||
"library": "Library",
|
"library": "Library",
|
||||||
"loading": {
|
"loading": {
|
||||||
"generic": "Loading…"
|
"generic": "Loading…"
|
||||||
@@ -772,6 +777,7 @@
|
|||||||
},
|
},
|
||||||
"megabits_per_second": "{value} megabits per second",
|
"megabits_per_second": "{value} megabits per second",
|
||||||
"metadata": "Metadata",
|
"metadata": "Metadata",
|
||||||
|
"most_active_studios": "Most Active Studios",
|
||||||
"movie": "Movie",
|
"movie": "Movie",
|
||||||
"movie_scene_number": "Movie Scene Number",
|
"movie_scene_number": "Movie Scene Number",
|
||||||
"movies": "Movies",
|
"movies": "Movies",
|
||||||
@@ -902,6 +908,7 @@
|
|||||||
},
|
},
|
||||||
"stash_id": "Stash ID",
|
"stash_id": "Stash ID",
|
||||||
"stash_ids": "Stash IDs",
|
"stash_ids": "Stash IDs",
|
||||||
|
"statistics": "Statistics",
|
||||||
"stats": {
|
"stats": {
|
||||||
"image_size": "Images size",
|
"image_size": "Images size",
|
||||||
"scenes_duration": "Scenes duration",
|
"scenes_duration": "Scenes duration",
|
||||||
|
|||||||
@@ -1505,6 +1505,13 @@
|
|||||||
"@types/react-dom" "*"
|
"@types/react-dom" "*"
|
||||||
"@types/react-transition-group" "*"
|
"@types/react-transition-group" "*"
|
||||||
|
|
||||||
|
"@types/react-slick@^0.23.8":
|
||||||
|
version "0.23.8"
|
||||||
|
resolved "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.8.tgz"
|
||||||
|
integrity sha512-SfzSg++/3uyftVZaCgHpW+2fnJFsyJEQ/YdsuqfOWQ5lqUYV/gY/UwAnkw4qksCj5jalto/T5rKXJ8zeFldQeA==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-transition-group@*", "@types/react-transition-group@^4.4.0":
|
"@types/react-transition-group@*", "@types/react-transition-group@^4.4.0":
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
||||||
@@ -2906,6 +2913,11 @@ end-of-stream@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
|
enquire.js@^2.1.6:
|
||||||
|
version "2.1.6"
|
||||||
|
resolved "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz"
|
||||||
|
integrity sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ=
|
||||||
|
|
||||||
enquirer@^2.3.5:
|
enquirer@^2.3.5:
|
||||||
version "2.3.6"
|
version "2.3.6"
|
||||||
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
|
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
|
||||||
@@ -4664,6 +4676,13 @@ json-to-pretty-yaml@^1.2.2:
|
|||||||
remedial "^1.0.7"
|
remedial "^1.0.7"
|
||||||
remove-trailing-spaces "^1.0.6"
|
remove-trailing-spaces "^1.0.6"
|
||||||
|
|
||||||
|
json2mq@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz"
|
||||||
|
integrity sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=
|
||||||
|
dependencies:
|
||||||
|
string-convert "^0.2.0"
|
||||||
|
|
||||||
json5@^1.0.1:
|
json5@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||||
@@ -4895,6 +4914,11 @@ lodash.clonedeep@^4.5.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||||
|
|
||||||
|
lodash.debounce@^4.0.8:
|
||||||
|
version "4.0.8"
|
||||||
|
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
|
||||||
|
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||||
|
|
||||||
lodash.get@^4:
|
lodash.get@^4:
|
||||||
version "4.4.2"
|
version "4.4.2"
|
||||||
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz"
|
||||||
@@ -6524,6 +6548,17 @@ react-side-effect@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
|
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
|
||||||
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
|
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
|
||||||
|
|
||||||
|
react-slick@^0.29.0:
|
||||||
|
version "0.29.0"
|
||||||
|
resolved "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz"
|
||||||
|
integrity sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==
|
||||||
|
dependencies:
|
||||||
|
classnames "^2.2.5"
|
||||||
|
enquire.js "^2.1.6"
|
||||||
|
json2mq "^0.2.0"
|
||||||
|
lodash.debounce "^4.0.8"
|
||||||
|
resize-observer-polyfill "^1.5.0"
|
||||||
|
|
||||||
react-transition-group@^4.3.0, react-transition-group@^4.4.1:
|
react-transition-group@^4.3.0, react-transition-group@^4.4.1:
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
||||||
@@ -6786,6 +6821,11 @@ requires-port@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||||
|
|
||||||
|
resize-observer-polyfill@^1.5.0:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
|
||||||
|
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||||
|
|
||||||
resolve-from@5.0.0, resolve-from@^5.0.0:
|
resolve-from@5.0.0, resolve-from@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
||||||
@@ -7043,6 +7083,11 @@ slice-ansi@^4.0.0:
|
|||||||
astral-regex "^2.0.0"
|
astral-regex "^2.0.0"
|
||||||
is-fullwidth-code-point "^3.0.0"
|
is-fullwidth-code-point "^3.0.0"
|
||||||
|
|
||||||
|
slick-carousel@^1.8.1:
|
||||||
|
version "1.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/slick-carousel/-/slick-carousel-1.8.1.tgz#a4bfb29014887bb66ce528b90bd0cda262cc8f8d"
|
||||||
|
integrity sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==
|
||||||
|
|
||||||
snake-case@^3.0.4:
|
snake-case@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz"
|
resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz"
|
||||||
@@ -7169,6 +7214,11 @@ strict-uri-encode@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
|
||||||
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||||
|
|
||||||
|
string-convert@^0.2.0:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
|
||||||
|
integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=
|
||||||
|
|
||||||
string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1:
|
string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user