Compare commits
13 Commits
v1.32.1
...
fix/relati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
781daa61a2 | ||
|
|
33e7691b94 | ||
|
|
d7b83d22ce | ||
|
|
b6eac0f364 | ||
|
|
c93467b3ac | ||
|
|
d954328911 | ||
|
|
3e43586acc | ||
|
|
7040da1334 | ||
|
|
9d10e6a88c | ||
|
|
8942eb8b7c | ||
|
|
51b05cd8fb | ||
|
|
374c78c989 | ||
|
|
507693881b |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -94,9 +94,9 @@ jobs:
|
||||
run: |
|
||||
failures=(neutral, skipped, timed_out, action_required)
|
||||
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
|
||||
echo ::set-output name=status::failure
|
||||
echo "status=failure" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ::set-output name=status::$WORKFLOW_CONCLUSION
|
||||
echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Post Status to Discord
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
|
||||
2
.github/workflows/preview.yml
vendored
2
.github/workflows/preview.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -67,9 +67,9 @@ jobs:
|
||||
run: |
|
||||
git fetch --prune --tags
|
||||
if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
|
||||
echo ::set-output name=RELEASE::stable
|
||||
echo "RELEASE=stable" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ::set-output name=RELEASE::edge
|
||||
echo "RELEASE=edge" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Set Up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
@@ -91,8 +91,9 @@ jobs:
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
- name: Publish Snap Package
|
||||
uses: snapcore/action-publish@v1
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
|
||||
with:
|
||||
store_login: ${{ secrets.SNAP_LOGIN }}
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
release: ${{ steps.prepare.outputs.RELEASE }}
|
||||
|
||||
@@ -109,9 +110,9 @@ jobs:
|
||||
run: |
|
||||
failures=(neutral, skipped, timed_out, action_required)
|
||||
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
|
||||
echo ::set-output name=status::failure
|
||||
echo "status=failure" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ::set-output name=status::$WORKFLOW_CONCLUSION
|
||||
echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Post Status to Discord
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
|
||||
11
.github/workflows/snap.yaml
vendored
11
.github/workflows/snap.yaml
vendored
@@ -35,9 +35,9 @@ jobs:
|
||||
run: |
|
||||
git fetch --prune --unshallow --tags
|
||||
if [[ $GITHUB_REF == refs/tags/* || $GITHUB_REF == refs/heads/master ]]; then
|
||||
echo ::set-output name=RELEASE::stable
|
||||
echo "RELEASE=stable" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ::set-output name=RELEASE::edge
|
||||
echo "RELEASE=edge" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Set Up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
@@ -57,8 +57,9 @@ jobs:
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
- name: Publish Snap Package
|
||||
uses: snapcore/action-publish@v1
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_LOGIN }}
|
||||
with:
|
||||
store_login: ${{ secrets.SNAP_LOGIN }}
|
||||
snap: ${{ steps.build.outputs.snap }}
|
||||
release: ${{ steps.prepare.outputs.RELEASE }}
|
||||
|
||||
@@ -75,9 +76,9 @@ jobs:
|
||||
run: |
|
||||
failures=(neutral, skipped, timed_out, action_required)
|
||||
if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then
|
||||
echo ::set-output name=status::failure
|
||||
echo "status=failure" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo ::set-output name=status::$WORKFLOW_CONCLUSION
|
||||
echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Post Status to Discord
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('Discover Customization', () => {
|
||||
.should('be.disabled');
|
||||
|
||||
cy.get('#data').clear();
|
||||
cy.get('#data').type('time travel{enter}', { delay: 100 });
|
||||
cy.get('#data').type('christmas{enter}', { delay: 100 });
|
||||
|
||||
// Confirming we have some results
|
||||
cy.contains('.slider-header', sliderTitle)
|
||||
|
||||
@@ -19,5 +19,6 @@ module.exports = {
|
||||
},
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
largePageDataBytes: 256000,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4418,7 +4418,7 @@ paths:
|
||||
- in: query
|
||||
name: genre
|
||||
schema:
|
||||
type: number
|
||||
type: string
|
||||
example: 18
|
||||
- in: query
|
||||
name: network
|
||||
|
||||
@@ -226,12 +226,13 @@ class PlexAPI {
|
||||
id: string,
|
||||
options: { addedAt: number } = {
|
||||
addedAt: Date.now() - 1000 * 60 * 60,
|
||||
}
|
||||
},
|
||||
mediaType: 'movie' | 'show'
|
||||
): Promise<PlexLibraryItem[]> {
|
||||
const response = await this.plexClient.query<PlexLibraryResponse>({
|
||||
uri: `/library/sections/${id}/all?sort=addedAt%3Adesc&addedAt>>=${Math.floor(
|
||||
options.addedAt / 1000
|
||||
)}`,
|
||||
uri: `/library/sections/${id}/all?type=${
|
||||
mediaType === 'show' ? '4' : '1'
|
||||
}&sort=addedAt%3Adesc&addedAt>>=${Math.floor(options.addedAt / 1000)}`,
|
||||
extraHeaders: {
|
||||
'X-Plex-Container-Start': `0`,
|
||||
'X-Plex-Container-Size': `500`,
|
||||
|
||||
@@ -85,7 +85,7 @@ interface DiscoverTvOptions {
|
||||
voteAverageLte?: string;
|
||||
includeEmptyReleaseDate?: boolean;
|
||||
originalLanguage?: string;
|
||||
genre?: number;
|
||||
genre?: string;
|
||||
network?: number;
|
||||
keywords?: string;
|
||||
sortBy?: SortOptions;
|
||||
|
||||
@@ -18,14 +18,14 @@ type ImageResponse = {
|
||||
imageBuffer: Buffer;
|
||||
};
|
||||
|
||||
const baseCacheDirectory = process.env.CONFIG_DIRECTORY
|
||||
? `${process.env.CONFIG_DIRECTORY}/cache/images`
|
||||
: path.join(__dirname, '../../config/cache/images');
|
||||
|
||||
class ImageProxy {
|
||||
public static async clearCache(key: string) {
|
||||
let deletedImages = 0;
|
||||
const cacheDirectory = path.join(
|
||||
__dirname,
|
||||
'../../config/cache/images/',
|
||||
key
|
||||
);
|
||||
const cacheDirectory = path.join(baseCacheDirectory, key);
|
||||
|
||||
const files = await promises.readdir(cacheDirectory);
|
||||
|
||||
@@ -57,11 +57,7 @@ class ImageProxy {
|
||||
public static async getImageStats(
|
||||
key: string
|
||||
): Promise<{ size: number; imageCount: number }> {
|
||||
const cacheDirectory = path.join(
|
||||
__dirname,
|
||||
'../../config/cache/images/',
|
||||
key
|
||||
);
|
||||
const cacheDirectory = path.join(baseCacheDirectory, key);
|
||||
|
||||
const imageTotalSize = await ImageProxy.getDirectorySize(cacheDirectory);
|
||||
const imageCount = await ImageProxy.getImageCount(cacheDirectory);
|
||||
@@ -263,7 +259,7 @@ class ImageProxy {
|
||||
}
|
||||
|
||||
private getCacheDirectory() {
|
||||
return path.join(__dirname, '../../config/cache/images/', this.key);
|
||||
return path.join(baseCacheDirectory, this.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ class PlexScanner
|
||||
// We remove 10 minutes from the last scan as a buffer
|
||||
addedAt: library.lastScan - 1000 * 60 * 10,
|
||||
}
|
||||
: undefined
|
||||
: undefined,
|
||||
library.type
|
||||
);
|
||||
|
||||
// Bundle items up by rating keys
|
||||
|
||||
@@ -356,7 +356,7 @@ discoverRoutes.get('/tv', async (req, res, next) => {
|
||||
page: Number(query.page),
|
||||
sortBy: query.sortBy as SortOptions,
|
||||
language: req.locale ?? query.language,
|
||||
genre: query.genre ? Number(query.genre) : undefined,
|
||||
genre: query.genre,
|
||||
network: query.network ? Number(query.network) : undefined,
|
||||
firstAirDateLte: query.firstAirDateLte
|
||||
? new Date(query.firstAirDateLte).toISOString().split('T')[0]
|
||||
@@ -491,7 +491,7 @@ discoverRoutes.get<{ genreId: string }>(
|
||||
const data = await tmdb.getDiscoverTv({
|
||||
page: Number(req.query.page),
|
||||
language: req.locale ?? (req.query.language as string),
|
||||
genre: Number(req.params.genreId),
|
||||
genre: req.params.genreId,
|
||||
});
|
||||
|
||||
const media = await Media.getRelatedMedia(
|
||||
@@ -770,7 +770,9 @@ discoverRoutes.get<{ language: string }, GenreSliderItem[]>(
|
||||
|
||||
await Promise.all(
|
||||
genres.map(async (genre) => {
|
||||
const genreData = await tmdb.getDiscoverTv({ genre: genre.id });
|
||||
const genreData = await tmdb.getDiscoverTv({
|
||||
genre: genre.id.toString(),
|
||||
});
|
||||
|
||||
mappedGenres.push({
|
||||
id: genre.id,
|
||||
|
||||
@@ -15,7 +15,9 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
const intl = useIntl();
|
||||
const dAirDate = new Date(airDate);
|
||||
const nowDate = new Date();
|
||||
const alreadyAired = dAirDate.getTime() < nowDate.getTime();
|
||||
const dAirDateOffset =
|
||||
dAirDate.getTime() - dAirDate.getTimezoneOffset() * -60000;
|
||||
const alreadyAired = dAirDateOffset < nowDate.getTime();
|
||||
|
||||
const compareWeek = new Date(
|
||||
alreadyAired ? Date.now() - WEEK : Date.now() + WEEK
|
||||
@@ -24,12 +26,16 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
let showRelative = false;
|
||||
|
||||
if (
|
||||
(alreadyAired && dAirDate.getTime() > compareWeek.getTime()) ||
|
||||
(!alreadyAired && dAirDate.getTime() < compareWeek.getTime())
|
||||
(alreadyAired && dAirDateOffset > compareWeek.getTime()) ||
|
||||
(!alreadyAired && dAirDateOffset < compareWeek.getTime())
|
||||
) {
|
||||
showRelative = true;
|
||||
}
|
||||
|
||||
const relativeTime = (dAirDateOffset - Date.now()) / 1000;
|
||||
|
||||
const within24Hours = Math.floor(Math.abs(relativeTime / (60 * 60))) <= 24;
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge badgeType="light">
|
||||
@@ -37,6 +43,7 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</Badge>
|
||||
{showRelative && (
|
||||
@@ -44,9 +51,15 @@ const AirDateBadge = ({ airDate }: AirDateBadgeProps) => {
|
||||
{intl.formatMessage(
|
||||
alreadyAired ? messages.airedrelative : messages.airsrelative,
|
||||
{
|
||||
relativeTime: (
|
||||
relativeTime: within24Hours ? (
|
||||
alreadyAired ? (
|
||||
'today'
|
||||
) : (
|
||||
'tomorrow'
|
||||
)
|
||||
) : (
|
||||
<FormattedRelativeTime
|
||||
value={(dAirDate.getTime() - Date.now()) / 1000}
|
||||
value={relativeTime}
|
||||
numeric="auto"
|
||||
updateIntervalInSeconds={1}
|
||||
/>
|
||||
|
||||
@@ -91,10 +91,10 @@ const DiscoverTv = () => {
|
||||
<option value={SortOptions.PopularityAsc}>
|
||||
{intl.formatMessage(messages.sortPopularityAsc)}
|
||||
</option>
|
||||
<option value={SortOptions.ReleaseDateDesc}>
|
||||
<option value={SortOptions.FirstAirDateDesc}>
|
||||
{intl.formatMessage(messages.sortFirstAirDateDesc)}
|
||||
</option>
|
||||
<option value={SortOptions.ReleaseDateAsc}>
|
||||
<option value={SortOptions.FirstAirDateAsc}>
|
||||
{intl.formatMessage(messages.sortFirstAirDateAsc)}
|
||||
</option>
|
||||
<option value={SortOptions.TmdbRatingDesc}>
|
||||
|
||||
@@ -167,27 +167,29 @@ const MobileMenu = () => {
|
||||
</Transition>
|
||||
<div className="padding-bottom-safe border-t border-gray-600 bg-gray-800 bg-opacity-90 backdrop-blur">
|
||||
<div className="flex h-full items-center justify-between px-6 py-4 text-gray-100">
|
||||
{filteredLinks.slice(0, 4).map((link) => {
|
||||
const isActive =
|
||||
router.pathname.match(link.activeRegExp) && !isOpen;
|
||||
return (
|
||||
<Link key={`mobile-menu-link-${link.href}`} href={link.href}>
|
||||
<a
|
||||
className={`flex flex-col items-center space-y-1 ${
|
||||
isActive ? 'text-indigo-500' : ''
|
||||
}`}
|
||||
>
|
||||
{cloneElement(
|
||||
isActive ? link.svgIconSelected : link.svgIcon,
|
||||
{
|
||||
className: 'h-6 w-6',
|
||||
}
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
{filteredLinks.length > 4 && (
|
||||
{filteredLinks
|
||||
.slice(0, filteredLinks.length === 5 ? 5 : 4)
|
||||
.map((link) => {
|
||||
const isActive =
|
||||
router.pathname.match(link.activeRegExp) && !isOpen;
|
||||
return (
|
||||
<Link key={`mobile-menu-link-${link.href}`} href={link.href}>
|
||||
<a
|
||||
className={`flex flex-col items-center space-y-1 ${
|
||||
isActive ? 'text-indigo-500' : ''
|
||||
}`}
|
||||
>
|
||||
{cloneElement(
|
||||
isActive ? link.svgIconSelected : link.svgIcon,
|
||||
{
|
||||
className: 'h-6 w-6',
|
||||
}
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
{filteredLinks.length > 4 && filteredLinks.length !== 5 && (
|
||||
<button
|
||||
className={`flex flex-col items-center space-y-1 ${
|
||||
isOpen ? 'text-indigo-500' : ''
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Button from '@app/components/Common/Button';
|
||||
import ConfirmButton from '@app/components/Common/ConfirmButton';
|
||||
import SlideOver from '@app/components/Common/SlideOver';
|
||||
import Tooltip from '@app/components/Common/Tooltip';
|
||||
import DownloadBlock from '@app/components/DownloadBlock';
|
||||
import IssueBlock from '@app/components/IssueBlock';
|
||||
import RequestBlock from '@app/components/RequestBlock';
|
||||
@@ -144,20 +145,24 @@ const ManageSlideOver = ({
|
||||
<div className="overflow-hidden rounded-md border border-gray-700 shadow">
|
||||
<ul>
|
||||
{data.mediaInfo?.downloadStatus?.map((status, index) => (
|
||||
<li
|
||||
<Tooltip
|
||||
key={`dl-status-${status.externalId}-${index}`}
|
||||
className="border-b border-gray-700 last:border-b-0"
|
||||
content={status.title}
|
||||
>
|
||||
<DownloadBlock downloadItem={status} />
|
||||
</li>
|
||||
<li className="border-b border-gray-700 last:border-b-0">
|
||||
<DownloadBlock downloadItem={status} />
|
||||
</li>
|
||||
</Tooltip>
|
||||
))}
|
||||
{data.mediaInfo?.downloadStatus4k?.map((status, index) => (
|
||||
<li
|
||||
<Tooltip
|
||||
key={`dl-status-${status.externalId}-${index}`}
|
||||
className="border-b border-gray-700 last:border-b-0"
|
||||
content={status.title}
|
||||
>
|
||||
<DownloadBlock downloadItem={status} is4k />
|
||||
</li>
|
||||
<li className="border-b border-gray-700 last:border-b-0">
|
||||
<DownloadBlock downloadItem={status} is4k />
|
||||
</li>
|
||||
</Tooltip>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -621,6 +621,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</span>
|
||||
</span>
|
||||
@@ -640,6 +641,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -91,11 +91,13 @@ const PersonDetails = () => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
}),
|
||||
deathdate: intl.formatDate(data.deathday, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
}),
|
||||
})
|
||||
);
|
||||
@@ -106,6 +108,7 @@ const PersonDetails = () => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -844,6 +844,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
@@ -858,6 +859,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -453,7 +453,15 @@
|
||||
}
|
||||
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
@apply rounded-full bg-indigo-500;
|
||||
@apply rounded-full border-0 bg-indigo-500;
|
||||
pointer-events: all;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type='range']::-moz-range-thumb {
|
||||
@apply rounded-full border-0 bg-indigo-500;
|
||||
pointer-events: all;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
Reference in New Issue
Block a user