mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat: add anime specific sorting and add help box to sort menu
This commit is contained in:
@@ -314,6 +314,7 @@ export const UserDataSchema = z.object({
|
||||
// results must be from either a movie or series search, so we can safely apply different sort criteria.
|
||||
movies: z.array(SortCriterion).optional(),
|
||||
series: z.array(SortCriterion).optional(),
|
||||
anime: z.array(SortCriterion).optional(),
|
||||
// cached and uncached results are a sort criteria themselves, so this can only be applied when cache is high enough in the global
|
||||
// sort criteria, and we would have to split the results into two (cached and uncached) lists, and then apply both sort criteria below
|
||||
// and then merge the results.
|
||||
@@ -323,6 +324,8 @@ export const UserDataSchema = z.object({
|
||||
uncachedMovies: z.array(SortCriterion).optional(),
|
||||
cachedSeries: z.array(SortCriterion).optional(),
|
||||
uncachedSeries: z.array(SortCriterion).optional(),
|
||||
cachedAnime: z.array(SortCriterion).optional(),
|
||||
uncachedAnime: z.array(SortCriterion).optional(),
|
||||
}),
|
||||
rpdbApiKey: z.string().optional(),
|
||||
formatter: Formatter,
|
||||
|
||||
+30
-13
@@ -2391,15 +2391,16 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
}
|
||||
|
||||
private sortStreams(streams: ParsedStream[], type: string): ParsedStream[] {
|
||||
let sortCriteria = this.userData.sortCriteria.global;
|
||||
let primarySortCriteria = this.userData.sortCriteria.global;
|
||||
let cachedSortCriteria = this.userData.sortCriteria.cached;
|
||||
let uncachedSortCriteria = this.userData.sortCriteria.uncached;
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
if (type === 'movie' && this.userData.sortCriteria?.movies?.length) {
|
||||
logger.info('Using movie sort criteria');
|
||||
sortCriteria = this.userData.sortCriteria?.movies;
|
||||
if (type === 'movie') {
|
||||
if (this.userData.sortCriteria?.movies?.length) {
|
||||
primarySortCriteria = this.userData.sortCriteria?.movies;
|
||||
}
|
||||
if (this.userData.sortCriteria?.cachedMovies?.length) {
|
||||
cachedSortCriteria = this.userData.sortCriteria?.cachedMovies;
|
||||
}
|
||||
@@ -2408,9 +2409,10 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'series' && this.userData.sortCriteria?.series?.length) {
|
||||
logger.info('Using series sort criteria');
|
||||
sortCriteria = this.userData.sortCriteria?.series;
|
||||
if (type === 'series') {
|
||||
if (this.userData.sortCriteria?.series?.length) {
|
||||
primarySortCriteria = this.userData.sortCriteria?.series;
|
||||
}
|
||||
if (this.userData.sortCriteria?.cachedSeries?.length) {
|
||||
cachedSortCriteria = this.userData.sortCriteria?.cachedSeries;
|
||||
}
|
||||
@@ -2418,13 +2420,26 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
uncachedSortCriteria = this.userData.sortCriteria?.uncachedSeries;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'anime') {
|
||||
if (this.userData.sortCriteria?.anime?.length) {
|
||||
primarySortCriteria = this.userData.sortCriteria?.anime;
|
||||
}
|
||||
if (this.userData.sortCriteria?.cachedAnime?.length) {
|
||||
cachedSortCriteria = this.userData.sortCriteria?.cachedAnime;
|
||||
}
|
||||
if (this.userData.sortCriteria?.uncachedAnime?.length) {
|
||||
uncachedSortCriteria = this.userData.sortCriteria?.uncachedAnime;
|
||||
}
|
||||
}
|
||||
|
||||
let sortedStreams = [];
|
||||
|
||||
if (
|
||||
cachedSortCriteria?.length &&
|
||||
uncachedSortCriteria?.length &&
|
||||
sortCriteria.length > 0 &&
|
||||
sortCriteria[0].key === 'cached'
|
||||
primarySortCriteria.length > 0 &&
|
||||
primarySortCriteria[0].key === 'cached'
|
||||
) {
|
||||
logger.info(
|
||||
'Splitting streams into cached and uncached and using separate sort criteria'
|
||||
@@ -2457,16 +2472,18 @@ ${errorStreams.length > 0 ? ` ❌ Errors : ${errorStreams.map((s) => `
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (sortCriteria[0].direction === 'desc') {
|
||||
if (primarySortCriteria[0].direction === 'desc') {
|
||||
sortedStreams = [...cachedSorted, ...uncachedSorted];
|
||||
} else {
|
||||
sortedStreams = [...uncachedSorted, ...cachedSorted];
|
||||
}
|
||||
} else {
|
||||
logger.debug(`using sort criteria: ${JSON.stringify(sortCriteria)}`);
|
||||
logger.debug(
|
||||
`using sort criteria: ${JSON.stringify(primarySortCriteria)}`
|
||||
);
|
||||
sortedStreams = streams.slice().sort((a, b) => {
|
||||
const aKey = this.dynamicSortKey(a, sortCriteria, type);
|
||||
const bKey = this.dynamicSortKey(b, sortCriteria, type);
|
||||
const aKey = this.dynamicSortKey(a, primarySortCriteria, type);
|
||||
const bKey = this.dynamicSortKey(b, primarySortCriteria, type);
|
||||
|
||||
for (let i = 0; i < aKey.length; i++) {
|
||||
if (aKey[i] < bKey[i]) return -1;
|
||||
|
||||
@@ -1437,11 +1437,6 @@ function SortableCatalogItem({
|
||||
|
||||
{/* Desktop Controls - only visible on medium screens and up */}
|
||||
<div className="hidden md:flex items-center justify-end gap-2 absolute top-4 right-4">
|
||||
<Switch
|
||||
value={catalog.enabled ?? true}
|
||||
onValueChange={onToggleEnabled}
|
||||
moreHelp="Enable or disable this catalog from being used"
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<IconButton
|
||||
rounded
|
||||
@@ -1458,6 +1453,11 @@ function SortableCatalogItem({
|
||||
title="Move to bottom"
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
value={catalog.enabled ?? true}
|
||||
onValueChange={onToggleEnabled}
|
||||
moreHelp="Enable or disable this catalog from being used"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@ import {
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { ArrowDownAZ, ArrowUpAZ } from 'lucide-react';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionTrigger,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
} from '../ui/accordion';
|
||||
|
||||
export function SortingMenu() {
|
||||
return (
|
||||
@@ -212,9 +218,72 @@ function Content() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="border rounded-[--radius-md]"
|
||||
triggerClass="dark:bg-[--paper]"
|
||||
contentClass="!pt-2 dark:bg-[--paper]"
|
||||
>
|
||||
<AccordionItem value="how-sorting-works">
|
||||
<AccordionTrigger className="bg-gray-900 rounded-[--radius-md]">
|
||||
How Sorting Works
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="space-y-4">
|
||||
<p>
|
||||
The sorting system uses a hierarchical approach to determine
|
||||
how content is sorted:
|
||||
</p>
|
||||
<ol className="list-decimal list-inside space-y-2">
|
||||
<li>
|
||||
<span className="font-medium">Primary Sorts:</span> Define
|
||||
the main sorting order for different content types:
|
||||
<ul className="list-disc list-inside ml-4 mt-1 space-y-1">
|
||||
<li>
|
||||
Global: Used as a fallback when no specific type sort is
|
||||
defined (You most likely just want to define this and
|
||||
ignore everything else.)
|
||||
</li>
|
||||
<li>Movies: Specific sorting for movies</li>
|
||||
<li>Series: Specific sorting for TV series</li>
|
||||
<li>Anime: Specific sorting for anime content</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-medium">
|
||||
Cached/Uncached Sorting:
|
||||
</span>{' '}
|
||||
If your primary sort has "cached" at the top:
|
||||
<ul className="list-disc list-inside ml-4 mt-1 space-y-1">
|
||||
<li>Content is split into cached and uncached groups</li>
|
||||
<li>
|
||||
Each group is sorted separately using its specific sort
|
||||
criteria
|
||||
</li>
|
||||
<li>
|
||||
Groups are then combined based on the cached sort
|
||||
direction
|
||||
</li>
|
||||
<li>
|
||||
Type-specific cached/uncached sorts (e.g., "Cached
|
||||
Movies") take priority over global cached/uncached sorts
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p className="text-sm text-[--muted]">
|
||||
Note: The system will use the most specific sort criteria
|
||||
available for the content type and cache status.
|
||||
</p>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
||||
<SettingsCard
|
||||
title="Type"
|
||||
description="Here, you can define different sort criteria for different types of content. Most people will only need to define the global sort criteria, and nothing else. However, if you wanted different sorting for cached and uncached content, you can do that here. You have to define the 'main' criteria to have cached at the top, and then separately define the cached and uncached criteria. If you also want to define different sorting for movies and series, you would do the same."
|
||||
description="Choose the type of sort criteria you want to currently edit."
|
||||
>
|
||||
<Select
|
||||
label="Sort Order Type"
|
||||
@@ -222,12 +291,15 @@ function Content() {
|
||||
{ label: 'Global', value: 'global' },
|
||||
{ label: 'Series', value: 'series' },
|
||||
{ label: 'Movies', value: 'movies' },
|
||||
{ label: 'Anime', value: 'anime' },
|
||||
{ label: 'Cached', value: 'cached' },
|
||||
{ label: 'Uncached', value: 'uncached' },
|
||||
{ label: 'Cached Movies', value: 'cachedMovies' },
|
||||
{ label: 'Uncached Movies', value: 'uncachedMovies' },
|
||||
{ label: 'Cached Series', value: 'cachedSeries' },
|
||||
{ label: 'Uncached Series', value: 'uncachedSeries' },
|
||||
{ label: 'Cached Anime', value: 'cachedAnime' },
|
||||
{ label: 'Uncached Anime', value: 'uncachedAnime' },
|
||||
]}
|
||||
value={currentSortType}
|
||||
onValueChange={setCurrentSortType}
|
||||
|
||||
Reference in New Issue
Block a user