Merge branch 'refs/heads/development' into non-hi-only

This commit is contained in:
morpheus65535 2024-06-09 08:24:50 -04:00
commit d3301a210f
221 changed files with 4786 additions and 1206 deletions

View file

@ -8,6 +8,8 @@ from app.database import TableShows, TableMovies, database, select
from ..utils import authenticate
import textdistance
api_ns_system_searches = Namespace('System Searches', description='Search for series or movies by name')
@ -61,4 +63,6 @@ class Searches(Resource):
results.append(result)
# sort results by how closely they match the query
results = sorted(results, key=lambda x: textdistance.hamming.distance(query, x['title']))
return results

View file

@ -29,6 +29,7 @@ from dogpile.cache.api import NO_VALUE
logger = logging.getLogger(__name__)
class LegendasdivxSubtitle(Subtitle):
"""Legendasdivx Subtitle."""
provider_name = 'legendasdivx'
@ -69,10 +70,12 @@ class LegendasdivxSubtitle(Subtitle):
self.wrong_fps = True
if self.skip_wrong_fps:
logger.debug("Legendasdivx :: Skipping subtitle due to FPS mismatch (expected: %s, got: %s)", video.fps, self.sub_frame_rate)
logger.debug("Legendasdivx :: Skipping subtitle due to FPS mismatch (expected: %s, got: %s)", video.fps,
self.sub_frame_rate)
# not a single match :)
return set()
logger.debug("Legendasdivx :: Frame rate mismatch (expected: %s, got: %s, but continuing...)", video.fps, self.sub_frame_rate)
logger.debug("Legendasdivx :: Frame rate mismatch (expected: %s, got: %s, but continuing...)", video.fps,
self.sub_frame_rate)
description = sanitize(self.description)
@ -112,6 +115,11 @@ class LegendasdivxSubtitle(Subtitle):
matches.update(['season'])
if video.episode and 'e{:02d}'.format(video.episode) in description:
matches.update(['episode'])
# All the search is already based on the series_imdb_id when present in the video and controlled via the
# the legendasdivx backend it, so if there is a result, it matches, either inside of a pack or a specific
# series and episode, so we can assume the season and episode matches.
if video.series_imdb_id:
matches.update(['series', 'series_imdb_id', 'season', 'episode'])
# release_group
if video.release_group and sanitize_release_group(video.release_group) in sanitize_release_group(description):
@ -121,6 +129,7 @@ class LegendasdivxSubtitle(Subtitle):
return matches
class LegendasdivxProvider(Provider):
"""Legendasdivx Provider."""
languages = {Language('por', 'BR')} | {Language('por')}
@ -135,7 +144,7 @@ class LegendasdivxProvider(Provider):
'Referer': 'https://www.legendasdivx.pt'
}
loginpage = site + '/forum/ucp.php?mode=login'
searchurl = site + '/modules.php?name=Downloads&file=jz&d_op=search&op=_jz00&query={query}'
searchurl = site + '/modules.php?name=Downloads&file=jz&d_op={d_op}&op={op}&query={query}&temporada={season}&episodio={episode}&imdb={imdbid}'
download_link = site + '/modules.php{link}'
def __init__(self, username, password, skip_wrong_fps=True):
@ -186,7 +195,8 @@ class LegendasdivxProvider(Provider):
res = self.session.post(self.loginpage, data)
res.raise_for_status()
# make sure we're logged in
logger.debug('Legendasdivx.pt :: Logged in successfully: PHPSESSID: %s', self.session.cookies.get_dict()['PHPSESSID'])
logger.debug('Legendasdivx.pt :: Logged in successfully: PHPSESSID: %s',
self.session.cookies.get_dict()['PHPSESSID'])
cj = self.session.cookies.copy()
store_cks = ("PHPSESSID", "phpbb3_2z8zs_sid", "phpbb3_2z8zs_k", "phpbb3_2z8zs_u", "lang")
for cn in iter(self.session.cookies.keys()):
@ -252,7 +262,7 @@ class LegendasdivxProvider(Provider):
continue
# get subtitle uploader
sub_header = _subbox.find("div", {"class" :"sub_header"})
sub_header = _subbox.find("div", {"class": "sub_header"})
uploader = sub_header.find("a").text if sub_header else 'anonymous'
exact_match = False
@ -278,12 +288,24 @@ class LegendasdivxProvider(Provider):
subtitles = []
# Set the default search criteria
d_op = 'search'
op = '_jz00'
lang_filter_key = 'form_cat'
if isinstance(video, Movie):
querytext = video.imdb_id if video.imdb_id else video.title
if isinstance(video, Episode):
querytext = '%22{}%20S{:02d}E{:02d}%22'.format(video.series, video.season, video.episode)
querytext = quote(querytext.lower())
# Overwrite the parameters to refine via imdb_id
if video.series_imdb_id:
querytext = '&faz=pesquisa_episodio'
lang_filter_key = 'idioma'
d_op = 'jz_00'
op = ''
else:
querytext = '%22{}%22%20S{:02d}E{:02d}'.format(video.series, video.season, video.episode)
querytext = quote(querytext.lower())
# language query filter
if not isinstance(languages, (tuple, list, set)):
@ -293,21 +315,30 @@ class LegendasdivxProvider(Provider):
logger.debug("Legendasdivx.pt :: searching for %s subtitles.", language)
language_id = language.opensubtitles
if 'por' in language_id:
lang_filter = '&form_cat=28'
lang_filter = '&{}=28'.format(lang_filter_key)
elif 'pob' in language_id:
lang_filter = '&form_cat=29'
lang_filter = '&{}=29'.format(lang_filter_key)
else:
lang_filter = ''
querytext = querytext + lang_filter if lang_filter else querytext
search_url = _searchurl.format(
query=querytext,
season=video.season,
episode=video.episode,
imdbid=video.series_imdb_id.replace('tt', '') if video.series_imdb_id else None,
op=op,
d_op=d_op,
)
try:
# sleep for a 1 second before another request
sleep(1)
searchLimitReached = False
self.headers['Referer'] = self.site + '/index.php'
self.session.headers.update(self.headers)
res = self.session.get(_searchurl.format(query=querytext), allow_redirects=False)
res = self.session.get(search_url, allow_redirects=False)
res.raise_for_status()
if res.status_code == 200 and "<!--pesquisas:" in res.text:
searches_count_groups = re.search(r'<!--pesquisas: (\d*)-->', res.text)
@ -327,7 +358,7 @@ class LegendasdivxProvider(Provider):
querytext = re.sub(r"(e|E)(\d{2})", "", querytext)
# sleep for a 1 second before another request
sleep(1)
res = self.session.get(_searchurl.format(query=querytext), allow_redirects=False)
res = self.session.get(search_url, allow_redirects=False)
res.raise_for_status()
if res.status_code == 200 and "<!--pesquisas:" in res.text:
searches_count_groups = re.search(r'<!--pesquisas: (\d*)-->', res.text)
@ -340,9 +371,11 @@ class LegendasdivxProvider(Provider):
if searches_count >= self.SAFE_SEARCH_LIMIT:
searchLimitReached = True
if (res.status_code == 200 and "A legenda não foi encontrada" in res.text):
logger.warning('Legendasdivx.pt :: query {0} return no results for language {1}(for series and season only).'.format(querytext, language_id))
logger.warning(
'Legendasdivx.pt :: query {0} return no results for language {1}(for series and season only).'.format(
querytext, language_id))
continue
if res.status_code == 302: # got redirected to login page.
if res.status_code == 302: # got redirected to login page.
# seems that our session cookies are no longer valid... clean them from cache
region.delete("legendasdivx_cookies2")
logger.debug("Legendasdivx.pt :: Logging in again. Cookies have expired!")
@ -350,7 +383,7 @@ class LegendasdivxProvider(Provider):
self.login()
# sleep for a 1 second before another request
sleep(1)
res = self.session.get(_searchurl.format(query=querytext))
res = self.session.get(search_url, allow_redirects=False)
res.raise_for_status()
if res.status_code == 200 and "<!--pesquisas:" in res.text:
searches_count_groups = re.search(r'<!--pesquisas: (\d*)-->', res.text)
@ -394,9 +427,9 @@ class LegendasdivxProvider(Provider):
# more pages?
if num_pages > 1:
for num_page in range(2, num_pages+1):
for num_page in range(2, num_pages + 1):
sleep(1) # another 1 sec before requesting...
_search_next = self.searchurl.format(query=querytext) + "&page={0}".format(str(num_page))
_search_next = search_url + "&page={0}".format(str(num_page))
logger.debug("Legendasdivx.pt :: Moving on to next page: %s", _search_next)
# sleep for a 1 second before another request
sleep(1)
@ -409,7 +442,7 @@ class LegendasdivxProvider(Provider):
def list_subtitles(self, video, languages):
return self.query(video, languages)
@reinitialize_on_error((RequestException,), attempts=1)
def download_subtitle(self, subtitle):
@ -478,7 +511,8 @@ class LegendasdivxProvider(Provider):
if isinstance(subtitle.video, Episode):
if all(key in _guess for key in ('season', 'episode')):
logger.debug("Legendasdivx.pt :: guessing %s", name)
logger.debug("Legendasdivx.pt :: subtitle S%sE%s video S%sE%s", _guess['season'], _guess['episode'], subtitle.video.season, subtitle.video.episode)
logger.debug("Legendasdivx.pt :: subtitle S%sE%s video S%sE%s", _guess['season'], _guess['episode'],
subtitle.video.season, subtitle.video.episode)
if subtitle.video.episode != _guess['episode'] or subtitle.video.season != _guess['season']:
logger.debug('Legendasdivx.pt :: subtitle does not match video, skipping')

View file

@ -13,7 +13,7 @@ import pysrt
import pysubs2
from bs4 import UnicodeDammit
from pysubs2 import SSAStyle
from pysubs2.subrip import parse_tags, MAX_REPRESENTABLE_TIME
from pysubs2.formats.subrip import parse_tags, MAX_REPRESENTABLE_TIME
from pysubs2.time import ms_to_times
from subzero.modification import SubtitleModifications
from subzero.language import Language

View file

@ -2,6 +2,13 @@
"rules": {
"no-console": "error",
"camelcase": "warn",
"no-restricted-imports": [
"error",
{
"patterns": ["..*"]
}
],
"simple-import-sort/imports": "error",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "warn",
"@typescript-eslint/no-empty-interface": "off",
@ -13,7 +20,7 @@
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"plugins": ["testing-library"],
"plugins": ["testing-library", "simple-import-sort"],
"overrides": [
{
"files": [
@ -21,6 +28,43 @@
"**/?(*.)+(spec|test).[jt]s?(x)"
],
"extends": ["plugin:testing-library/react"]
},
{
"files": ["*.ts", "*.tsx"],
"rules": {
"simple-import-sort/imports": [
"error",
{
"groups": [
[
// React Packages
"^react",
// Mantine Packages
"^@mantine/",
// Vendor Packages
"^(\\w|@\\w)",
// Side Effect Imports
"^\\u0000",
// Internal Packages
"^@/\\w",
// Parent Imports
"^\\.\\.(?!/?$)",
"^\\.\\./?$",
// Relative Imports
"^\\./(?=.*/)(?!/?$)",
"^\\.(?!/?$)",
"^\\./?$",
// Style Imports
"^.+\\.?(css)$"
]
]
}
]
}
}
]
],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": "latest"
}
}

View file

@ -19,7 +19,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.22.3",
"react-router-dom": "^6.23.1",
"socket.io-client": "^4.7.5"
},
"devDependencies": {
@ -45,6 +45,7 @@
"eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^12.1.0",
"eslint-plugin-testing-library": "^6.2.0",
"husky": "^9.0.11",
"jsdom": "^24.0.0",
@ -3111,9 +3112,9 @@
"dev": true
},
"node_modules/@remix-run/router": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
"integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==",
"engines": {
"node": ">=14.0.0"
}
@ -6101,6 +6102,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-simple-import-sort": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz",
"integrity": "sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"eslint": ">=5.0.0"
}
},
"node_modules/eslint-plugin-testing-library": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.2.0.tgz",
@ -8926,11 +8937,11 @@
}
},
"node_modules/react-router": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
"integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==",
"dependencies": {
"@remix-run/router": "1.15.3"
"@remix-run/router": "1.16.1"
},
"engines": {
"node": ">=14.0.0"
@ -8940,12 +8951,12 @@
}
},
"node_modules/react-router-dom": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz",
"integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==",
"dependencies": {
"@remix-run/router": "1.15.3",
"react-router": "6.22.3"
"@remix-run/router": "1.16.1",
"react-router": "6.23.1"
},
"engines": {
"node": ">=14.0.0"

View file

@ -23,7 +23,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.22.3",
"react-router-dom": "^6.23.1",
"socket.io-client": "^4.7.5"
},
"devDependencies": {
@ -49,6 +49,7 @@
"eslint": "^8.57.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^12.1.0",
"eslint-plugin-testing-library": "^6.2.0",
"husky": "^9.0.11",
"jsdom": "^24.0.0",
@ -72,6 +73,7 @@
"build": "vite build",
"build:ci": "vite build -m development",
"check": "eslint --ext .ts,.tsx src",
"check:fix": "eslint --ext .ts,.tsx src --fix",
"check:ts": "tsc --noEmit --incremental false",
"check:fmt": "prettier -c .",
"test": "vitest",

View file

@ -1,14 +1,4 @@
import { useSystem, useSystemSettings } from "@/apis/hooks";
import { Action, Search } from "@/components";
import { useNavbar } from "@/contexts/Navbar";
import { useIsOnline } from "@/contexts/Online";
import { Environment, useGotoHomepage } from "@/utilities";
import {
faArrowRotateLeft,
faGear,
faPowerOff,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FunctionComponent } from "react";
import {
Anchor,
AppShell,
@ -19,7 +9,17 @@ import {
Group,
Menu,
} from "@mantine/core";
import { FunctionComponent } from "react";
import {
faArrowRotateLeft,
faGear,
faPowerOff,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useSystem, useSystemSettings } from "@/apis/hooks";
import { Action, Search } from "@/components";
import { useNavbar } from "@/contexts/Navbar";
import { useIsOnline } from "@/contexts/Online";
import { Environment, useGotoHomepage } from "@/utilities";
import styles from "./Header.module.scss";
const AppHeader: FunctionComponent = () => {
@ -52,7 +52,7 @@ const AppHeader: FunctionComponent = () => {
size="sm"
hiddenFrom="sm"
></Burger>
<Badge size="lg" radius="sm">
<Badge size="lg" radius="sm" variant="brand">
Bazarr
</Badge>
</Group>

View file

@ -1,16 +1,12 @@
import { Action } from "@/components";
import { useNavbar } from "@/contexts/Navbar";
import { useRouteItems } from "@/Router";
import { CustomRouteObject, Route } from "@/Router/type";
import { BuildKey, pathJoin } from "@/utilities";
import { LOG } from "@/utilities/console";
import {
faHeart,
faMoon,
faSun,
IconDefinition,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, {
createContext,
FunctionComponent,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { matchPath, NavLink, RouteObject, useLocation } from "react-router-dom";
import {
Anchor,
AppShell,
@ -24,16 +20,20 @@ import {
useMantineColorScheme,
} from "@mantine/core";
import { useHover } from "@mantine/hooks";
import {
faHeart,
faMoon,
faSun,
IconDefinition,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import React, {
createContext,
FunctionComponent,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { matchPath, NavLink, RouteObject, useLocation } from "react-router-dom";
import { Action } from "@/components";
import { useNavbar } from "@/contexts/Navbar";
import { useRouteItems } from "@/Router";
import { CustomRouteObject, Route } from "@/Router/type";
import { BuildKey, pathJoin } from "@/utilities";
import { LOG } from "@/utilities/console";
import styles from "./Navbar.module.scss";
const Selection = createContext<{

View file

@ -1,3 +1,4 @@
import { FunctionComponent, PropsWithChildren } from "react";
import {
ActionIcon,
AppShell,
@ -5,16 +6,17 @@ import {
Button,
createTheme,
MantineProvider,
Pagination,
} from "@mantine/core";
import { FunctionComponent, PropsWithChildren } from "react";
import ThemeLoader from "@/App/ThemeLoader";
import "@mantine/core/styles.layer.css";
import "@mantine/notifications/styles.layer.css";
import styleVars from "@/assets/_variables.module.scss";
import buttonClasses from "@/assets/button.module.scss";
import actionIconClasses from "@/assets/action_icon.module.scss";
import appShellClasses from "@/assets/app_shell.module.scss";
import badgeClasses from "@/assets/badge.module.scss";
import buttonClasses from "@/assets/button.module.scss";
import paginationClasses from "@/assets/pagination.module.scss";
const themeProvider = createTheme({
fontFamily: "Roboto, open sans, Helvetica Neue, Helvetica, Arial, sans-serif",
@ -46,6 +48,9 @@ const themeProvider = createTheme({
Button: Button.extend({
classNames: buttonClasses,
}),
Pagination: Pagination.extend({
classNames: paginationClasses,
}),
},
});

View file

@ -1,5 +1,5 @@
import { render } from "@/tests";
import { describe, it } from "vitest";
import { render } from "@/tests";
import App from ".";
describe("App", () => {

View file

@ -1,16 +1,16 @@
import { FunctionComponent, useEffect, useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import { AppShell } from "@mantine/core";
import { useWindowEvent } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import AppNavbar from "@/App/Navbar";
import { RouterNames } from "@/Router/RouterNames";
import ErrorBoundary from "@/components/ErrorBoundary";
import NavbarProvider from "@/contexts/Navbar";
import OnlineProvider from "@/contexts/Online";
import { notification } from "@/modules/task";
import CriticalError from "@/pages/errors/CriticalError";
import { RouterNames } from "@/Router/RouterNames";
import { Environment } from "@/utilities";
import { AppShell } from "@mantine/core";
import { useWindowEvent } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import { FunctionComponent, useEffect, useState } from "react";
import { Outlet, useNavigate } from "react-router-dom";
import AppHeader from "./Header";
import styleVars from "@/assets/_variables.module.scss";

View file

@ -1,7 +1,7 @@
import { useSystemSettings } from "@/apis/hooks";
import { LoadingOverlay } from "@mantine/core";
import { FunctionComponent, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { LoadingOverlay } from "@mantine/core";
import { useSystemSettings } from "@/apis/hooks";
const Redirector: FunctionComponent = () => {
const { data } = useSystemSettings();

View file

@ -1,11 +1,29 @@
import App from "@/App";
import {
createContext,
FunctionComponent,
lazy,
useContext,
useMemo,
} from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import {
faClock,
faCogs,
faExclamationTriangle,
faFileExcel,
faFilm,
faLaptop,
faPlay,
} from "@fortawesome/free-solid-svg-icons";
import { useBadges } from "@/apis/hooks";
import { useEnabledStatus } from "@/apis/hooks/site";
import App from "@/App";
import { Lazy } from "@/components/async";
import Authentication from "@/pages/Authentication";
import BlacklistMoviesView from "@/pages/Blacklist/Movies";
import BlacklistSeriesView from "@/pages/Blacklist/Series";
import Episodes from "@/pages/Episodes";
import NotFound from "@/pages/errors/NotFound";
import MoviesHistoryView from "@/pages/History/Movies";
import SeriesHistoryView from "@/pages/History/Series";
import MovieView from "@/pages/Movies";
@ -30,25 +48,7 @@ import SystemReleasesView from "@/pages/System/Releases";
import SystemTasksView from "@/pages/System/Tasks";
import WantedMoviesView from "@/pages/Wanted/Movies";
import WantedSeriesView from "@/pages/Wanted/Series";
import NotFound from "@/pages/errors/NotFound";
import { Environment } from "@/utilities";
import {
faClock,
faCogs,
faExclamationTriangle,
faFileExcel,
faFilm,
faLaptop,
faPlay,
} from "@fortawesome/free-solid-svg-icons";
import {
FunctionComponent,
createContext,
lazy,
useContext,
useMemo,
} from "react";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import Redirector from "./Redirector";
import { RouterNames } from "./RouterNames";
import { CustomRouteObject } from "./type";

View file

@ -1,5 +1,5 @@
import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
import { RouteObject } from "react-router-dom";
import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
declare namespace Route {
export type Item = {

View file

@ -4,9 +4,9 @@ import {
useQuery,
useQueryClient,
} from "react-query";
import { usePaginationQuery } from "../queries/hooks";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { usePaginationQuery } from "@/apis/queries/hooks";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
const cacheEpisodes = (client: QueryClient, episodes: Item.Episode[]) => {
episodes.forEach((item) => {

View file

@ -1,6 +1,6 @@
import { useQuery } from "react-query";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
export function useHistoryStats(
time: History.TimeFrameOptions,

View file

@ -1,6 +1,6 @@
import { useQuery } from "react-query";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
export function useLanguages(history?: boolean) {
return useQuery(

View file

@ -4,9 +4,9 @@ import {
useQuery,
useQueryClient,
} from "react-query";
import { usePaginationQuery } from "../queries/hooks";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { usePaginationQuery } from "@/apis/queries/hooks";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
const cacheMovies = (client: QueryClient, movies: Item.Movie[]) => {
movies.forEach((item) => {

View file

@ -1,6 +1,6 @@
import { useMutation, useQuery, useQueryClient } from "react-query";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
export function useSystemProviders(history?: boolean) {
return useQuery(

View file

@ -4,9 +4,9 @@ import {
useQuery,
useQueryClient,
} from "react-query";
import { usePaginationQuery } from "../queries/hooks";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { usePaginationQuery } from "@/apis/queries/hooks";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
function cacheSeries(client: QueryClient, series: Item.Series[]) {
series.forEach((item) => {

View file

@ -1,5 +1,5 @@
import { useIsMutating } from "react-query";
import { QueryKeys } from "../queries/keys";
import { QueryKeys } from "@/apis/queries/keys";
export function useIsAnyActionRunning() {
return useIsMutating([QueryKeys.Actions]) > 0;

View file

@ -1,6 +1,6 @@
import { useMutation, useQuery, useQueryClient } from "react-query";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
export function useSubtitleAction() {
const client = useQueryClient();

View file

@ -1,9 +1,9 @@
import { Environment } from "@/utilities";
import { setAuthenticated } from "@/utilities/event";
import { useMemo } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { QueryKeys } from "../queries/keys";
import api from "../raw";
import { QueryKeys } from "@/apis/queries/keys";
import api from "@/apis/raw";
import { Environment } from "@/utilities";
import { setAuthenticated } from "@/utilities/event";
export function useBadges() {
return useQuery(

View file

@ -1,12 +1,12 @@
import { GetItemId, useOnValueChange } from "@/utilities";
import { usePageSize } from "@/utilities/storage";
import { useCallback, useEffect, useState } from "react";
import {
QueryKey,
UseQueryResult,
useQuery,
useQueryClient,
UseQueryResult,
} from "react-query";
import { GetItemId, useOnValueChange } from "@/utilities";
import { usePageSize } from "@/utilities/storage";
import { QueryKeys } from "./keys";
export type UsePaginationQueryResult<T extends object> = UseQueryResult<

View file

@ -1,10 +1,10 @@
import socketio from "@/modules/socketio";
import { notification } from "@/modules/task";
import { LOG } from "@/utilities/console";
import { setAuthenticated } from "@/utilities/event";
import { showNotification } from "@mantine/notifications";
import Axios, { AxiosError, AxiosInstance, CancelTokenSource } from "axios";
import { Environment } from "../../utilities";
import socketio from "@/modules/socketio";
import { notification } from "@/modules/task";
import { Environment } from "@/utilities";
import { LOG } from "@/utilities/console";
import { setAuthenticated } from "@/utilities/event";
function GetErrorMessage(data: unknown, defaultMsg = "Unknown error"): string {
if (typeof data === "string") {

View file

@ -9,6 +9,42 @@ $color-brand-7: #ae3ec9;
$color-brand-8: #9c36b5;
$color-brand-9: #862e9c;
// Based on Mantine Cyan
$color-highlight-0: #e3fafc;
$color-highlight-1: #c5f6fa;
$color-highlight-2: #99e9f2;
$color-highlight-3: #66d9e8;
$color-highlight-4: #3bc9db;
$color-highlight-5: #22b8cf;
$color-highlight-6: #15aabf;
$color-highlight-7: #1098ad;
$color-highlight-8: #0c8599;
$color-highlight-9: #0b7285;
// Based on Mantine Yellow
$color-warning-0: #fff9db;
$color-warning-1: #fff3bf;
$color-warning-2: #ffec99;
$color-warning-3: #ffe066;
$color-warning-4: #ffd43b;
$color-warning-5: #fcc419;
$color-warning-6: #fab005;
$color-warning-7: #f59f00;
$color-warning-8: #f08c00;
$color-warning-9: #e67700;
// Based on Mantine Gray
$color-disabled-0: #f8f9fa;
$color-disabled-1: #f1f3f5;
$color-disabled-2: #e9ecef;
$color-disabled-3: #dee2e6;
$color-disabled-4: #ced4da;
$color-disabled-5: #adb5bd;
$color-disabled-6: #868e96;
$color-disabled-7: #495057;
$color-disabled-8: #343a40;
$color-disabled-9: #212529;
$header-height: 64px;
:global {

View file

@ -1,14 +1,25 @@
@layer mantine {
.root {
background-color: transparent;
&[data-variant="light"] {
color: var(--mantine-color-dark-0);
}
&[data-variant="dark"] {
--ai-bg: transparent;
--ai-hover: darken(var(--mantine-color-grape-light), 0.2);
}
@include light {
&[data-variant="light"] {
background-color: var(--mantine-color-gray-1);
color: var(--mantine-color-dark-2);
}
&[data-variant="dark"] {
--ai-color: var(--mantine-color-dark-filled);
}
}
}
}

View file

@ -1,5 +1,7 @@
.main {
@include dark {
background-color: rgb(26, 27, 30);
@layer mantine {
.main {
@include dark {
background-color: var(--mantine-color-dark-8);
}
}
}

View file

@ -1,8 +1,40 @@
.root {
background-color: var(--mantine-color-grape-light);
@layer mantine {
.root {
background-color: transparentize($color-brand-6, 0.8);
@include light {
color: var(--mantine-color-dark-filled);
background-color: var(--mantine-color-grape-light);
&[data-variant="warning"] {
color: lighten($color-warning-2, 1);
background-color: transparentize($color-warning-6, 0.8);
}
&[data-variant="highlight"] {
color: lighten($color-highlight-2, 1);
background-color: transparentize($color-highlight-5, 0.8);
}
&[data-variant="disabled"] {
color: lighten($color-disabled-0, 1);
background-color: transparentize($color-disabled-7, 0.8);
}
@include light {
color: $color-brand-6;
background-color: transparentize($color-brand-3, 0.8);
&[data-variant="warning"] {
color: darken($color-warning-7, 1);
background-color: transparentize($color-warning-6, 0.8);
}
&[data-variant="disabled"] {
color: darken($color-disabled-6, 1);
background-color: transparentize($color-disabled-4, 0.8);
}
&[data-variant="highlight"] {
color: darken($color-highlight-6, 1);
background-color: transparentize($color-highlight-5, 0.8);
}
}
}
}

View file

@ -9,4 +9,10 @@
color: var(--mantine-color-red-0);
}
}
.root:disabled {
@include dark {
color: var(--mantine-color-dark-9);
}
}
}

View file

@ -0,0 +1,3 @@
.control {
--pagination-active-bg: var(--mantine-color-brand-filled);
}

View file

@ -1,5 +1,5 @@
import UIError from "@/pages/errors/UIError";
import { Component, PropsWithChildren } from "react";
import UIError from "@/pages/errors/UIError";
interface State {
error: Error | null;

View file

@ -1,15 +1,15 @@
import { useServerSearch } from "@/apis/hooks";
import { useDebouncedValue } from "@/utilities";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FunctionComponent, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import {
Anchor,
Autocomplete,
ComboboxItem,
OptionsFilter,
} from "@mantine/core";
import { FunctionComponent, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useServerSearch } from "@/apis/hooks";
import { useDebouncedValue } from "@/utilities";
import styles from "./Search.module.scss";
type SearchResultItem = {
@ -19,7 +19,7 @@ type SearchResultItem = {
function useSearch(query: string) {
const debouncedQuery = useDebouncedValue(query, 500);
const { data } = useServerSearch(debouncedQuery, debouncedQuery.length > 0);
const { data } = useServerSearch(debouncedQuery, debouncedQuery.length >= 0);
return useMemo<SearchResultItem[]>(
() =>
@ -32,7 +32,6 @@ function useSearch(query: string) {
} else {
throw new Error("Unknown search result");
}
return {
value: `${v.title} (${v.year})`,
link,
@ -92,6 +91,8 @@ const Search: FunctionComponent = () => {
size="sm"
data={results}
value={query}
scrollAreaProps={{ type: "auto" }}
maxDropdownHeight={400}
onChange={setQuery}
onBlur={() => setQuery("")}
filter={optionsFilter}

View file

@ -1,4 +1,6 @@
import { BuildKey } from "@/utilities";
import { FunctionComponent } from "react";
import { Group, List, Popover, Stack, Text } from "@mantine/core";
import { useHover } from "@mantine/hooks";
import {
faCheck,
faCheckCircle,
@ -7,9 +9,7 @@ import {
faTimes,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Group, List, Popover, Stack, Text } from "@mantine/core";
import { useHover } from "@mantine/hooks";
import { FunctionComponent } from "react";
import { BuildKey } from "@/utilities";
interface StateIconProps {
matches: string[];

View file

@ -1,11 +1,5 @@
import { useSubtitleAction } from "@/apis/hooks";
import { ColorToolModal } from "@/components/forms/ColorToolForm";
import { FrameRateModal } from "@/components/forms/FrameRateForm";
import { TimeOffsetModal } from "@/components/forms/TimeOffsetForm";
import { TranslationModal } from "@/components/forms/TranslationForm";
import { useModals } from "@/modules/modals";
import { ModalComponent } from "@/modules/modals/WithModal";
import { task } from "@/modules/task";
import { FunctionComponent, ReactElement, useCallback, useMemo } from "react";
import { Divider, List, Menu, MenuProps, ScrollArea } from "@mantine/core";
import {
faClock,
faCode,
@ -23,8 +17,14 @@ import {
IconDefinition,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Divider, List, Menu, MenuProps, ScrollArea } from "@mantine/core";
import { FunctionComponent, ReactElement, useCallback, useMemo } from "react";
import { useSubtitleAction } from "@/apis/hooks";
import { ColorToolModal } from "@/components/forms/ColorToolForm";
import { FrameRateModal } from "@/components/forms/FrameRateForm";
import { TimeOffsetModal } from "@/components/forms/TimeOffsetForm";
import { TranslationModal } from "@/components/forms/TranslationForm";
import { useModals } from "@/modules/modals";
import { ModalComponent } from "@/modules/modals/WithModal";
import { task } from "@/modules/task";
import { SyncSubtitleModal } from "./forms/SyncSubtitleForm";
export interface ToolOptions {

View file

@ -1,7 +1,7 @@
import { FunctionComponent, ReactElement } from "react";
import { Tooltip, TooltipProps } from "@mantine/core";
import { useHover } from "@mantine/hooks";
import { isNull, isUndefined } from "lodash";
import { FunctionComponent, ReactElement } from "react";
interface TextPopoverProps {
children: ReactElement;
@ -21,7 +21,12 @@ const TextPopover: FunctionComponent<TextPopoverProps> = ({
}
return (
<Tooltip opened={hovered} label={text} {...tooltip}>
<Tooltip
opened={hovered}
label={text}
{...tooltip}
style={{ textWrap: "pretty" }}
>
<div ref={ref}>{children}</div>
</Tooltip>
);

View file

@ -1,5 +1,5 @@
import { LoadingOverlay } from "@mantine/core";
import { FunctionComponent, PropsWithChildren, Suspense } from "react";
import { LoadingOverlay } from "@mantine/core";
const Lazy: FunctionComponent<PropsWithChildren> = ({ children }) => {
return <Suspense fallback={<LoadingOverlay visible />}>{children}</Suspense>;

View file

@ -1,7 +1,7 @@
import { useCallback, useState } from "react";
import { UseMutationResult } from "react-query";
import { Action } from "../inputs";
import { ActionProps } from "../inputs/Action";
import { Action } from "@/components/inputs";
import { ActionProps } from "@/components/inputs/Action";
type MutateActionProps<DATA, VAR> = Omit<
ActionProps,

View file

@ -1,6 +1,6 @@
import { Button, ButtonProps } from "@mantine/core";
import { useCallback, useState } from "react";
import { UseMutationResult } from "react-query";
import { Button, ButtonProps } from "@mantine/core";
type MutateButtonProps<DATA, VAR> = Omit<
ButtonProps,

View file

@ -1,7 +1,7 @@
import { LoadingProvider } from "@/contexts";
import { LoadingOverlay } from "@mantine/core";
import { FunctionComponent, ReactNode } from "react";
import { UseQueryResult } from "react-query";
import { LoadingOverlay } from "@mantine/core";
import { LoadingProvider } from "@/contexts";
interface QueryOverlayProps {
result: UseQueryResult<unknown, unknown>;

View file

@ -1,6 +1,6 @@
import { BuildKey } from "@/utilities";
import { Badge, BadgeProps, Group, GroupProps } from "@mantine/core";
import { FunctionComponent } from "react";
import { Badge, BadgeProps, Group, GroupProps } from "@mantine/core";
import { BuildKey } from "@/utilities";
export type AudioListProps = GroupProps & {
audios: Language.Info[];

View file

@ -1,3 +1,5 @@
import { FunctionComponent } from "react";
import { Tooltip } from "@mantine/core";
import {
faClock,
faClosedCaptioning,
@ -9,8 +11,6 @@ import {
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@mantine/core";
import { FunctionComponent } from "react";
enum HistoryAction {
Delete = 0,

View file

@ -1,5 +1,5 @@
import { render, screen } from "@/tests";
import { describe, it } from "vitest";
import { render, screen } from "@/tests";
import { Language } from ".";
describe("Language text", () => {

View file

@ -1,6 +1,6 @@
import { BuildKey } from "@/utilities";
import { Badge, Group, Text, TextProps } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Badge, Group, Text, TextProps } from "@mantine/core";
import { BuildKey } from "@/utilities";
type LanguageTextProps = TextProps & {
value: Language.Info;

View file

@ -1,5 +1,5 @@
import { useLanguageProfiles } from "@/apis/hooks";
import { FunctionComponent, useMemo } from "react";
import { useLanguageProfiles } from "@/apis/hooks";
interface Props {
index: number | null;

View file

@ -1,7 +1,7 @@
import { FunctionComponent, useMemo } from "react";
import { useLanguages } from "@/apis/hooks";
import { Selector, SelectorProps } from "@/components/inputs";
import { useSelectorOptions } from "@/utilities";
import { FunctionComponent, useMemo } from "react";
interface LanguageSelectorProps
extends Omit<SelectorProps<Language.Server>, "options" | "getkey"> {

View file

@ -1,11 +1,11 @@
import { FunctionComponent } from "react";
import { Button, Divider, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { useSubtitleAction } from "@/apis/hooks";
import { Selector, SelectorOption } from "@/components";
import { useModals, withModal } from "@/modules/modals";
import { task } from "@/modules/task";
import FormUtils from "@/utilities/form";
import { Button, Divider, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
const TaskName = "Changing Color";

View file

@ -1,10 +1,10 @@
import { FunctionComponent } from "react";
import { Button, Divider, Group, NumberInput, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { useSubtitleAction } from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { task } from "@/modules/task";
import FormUtils from "@/utilities/form";
import { Button, Divider, Group, NumberInput, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
const TaskName = "Changing Frame Rate";

View file

@ -1,11 +1,11 @@
import { FunctionComponent, useMemo } from "react";
import { UseMutationResult } from "react-query";
import { Button, Divider, Group, LoadingOverlay, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { useLanguageProfiles } from "@/apis/hooks";
import { MultiSelector, Selector } from "@/components/inputs";
import { useModals, withModal } from "@/modules/modals";
import { GetItemId, useSelectorOptions } from "@/utilities";
import { Button, Divider, Group, LoadingOverlay, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent, useMemo } from "react";
import { UseMutationResult } from "react-query";
interface Props {
mutation: UseMutationResult<void, unknown, FormType.ModifyItem, unknown>;

View file

@ -1,20 +1,5 @@
import { useMovieSubtitleModification } from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { TaskGroup, task } from "@/modules/task";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
useLanguageProfileBy,
useProfileItemsToLanguages,
} from "@/utilities/languages";
import {
faCheck,
faCircleNotch,
faInfoCircle,
faTimes,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FunctionComponent, useEffect, useMemo } from "react";
import { Column } from "react-table";
import {
Button,
Checkbox,
@ -24,12 +9,27 @@ import {
Text,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import {
faCheck,
faCircleNotch,
faInfoCircle,
faTimes,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { isString } from "lodash";
import { FunctionComponent, useEffect, useMemo } from "react";
import { Column } from "react-table";
import TextPopover from "../TextPopover";
import { Action, Selector } from "../inputs";
import { SimpleTable } from "../tables";
import { useMovieSubtitleModification } from "@/apis/hooks";
import { Action, Selector } from "@/components/inputs";
import { SimpleTable } from "@/components/tables";
import TextPopover from "@/components/TextPopover";
import { useModals, withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
useLanguageProfileBy,
useProfileItemsToLanguages,
} from "@/utilities/languages";
type SubtitleFile = {
file: File;

View file

@ -1,9 +1,5 @@
import { Action, Selector, SelectorOption, SimpleTable } from "@/components";
import { useModals, withModal } from "@/modules/modals";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import { LOG } from "@/utilities/console";
import FormUtils from "@/utilities/form";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FunctionComponent, useCallback, useMemo } from "react";
import { Column } from "react-table";
import {
Accordion,
Button,
@ -15,9 +11,13 @@ import {
TextInput,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent, useCallback, useMemo } from "react";
import { Column } from "react-table";
import ChipInput from "../inputs/ChipInput";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Action, Selector, SelectorOption, SimpleTable } from "@/components";
import ChipInput from "@/components/inputs/ChipInput";
import { useModals, withModal } from "@/modules/modals";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import { LOG } from "@/utilities/console";
import FormUtils from "@/utilities/form";
import styles from "./ProfileEditForm.module.scss";
export const anyCutoff = 65535;

View file

@ -1,24 +1,5 @@
import {
useEpisodesBySeriesId,
useEpisodeSubtitleModification,
useSubtitleInfos,
} from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
useLanguageProfileBy,
useProfileItemsToLanguages,
} from "@/utilities/languages";
import {
faCheck,
faCircleNotch,
faInfoCircle,
faTimes,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FunctionComponent, useEffect, useMemo } from "react";
import { Column } from "react-table";
import {
Button,
Checkbox,
@ -28,12 +9,31 @@ import {
Text,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import {
faCheck,
faCircleNotch,
faInfoCircle,
faTimes,
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { isString } from "lodash";
import { FunctionComponent, useEffect, useMemo } from "react";
import { Column } from "react-table";
import { Action, Selector } from "../inputs";
import { SimpleTable } from "../tables";
import TextPopover from "../TextPopover";
import {
useEpisodesBySeriesId,
useEpisodeSubtitleModification,
useSubtitleInfos,
} from "@/apis/hooks";
import { Action, Selector } from "@/components/inputs";
import { SimpleTable } from "@/components/tables";
import TextPopover from "@/components/TextPopover";
import { useModals, withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
import { useArrayAction, useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import {
useLanguageProfileBy,
useProfileItemsToLanguages,
} from "@/utilities/languages";
type SubtitleFile = {
file: File;

View file

@ -1,20 +1,19 @@
/* eslint-disable camelcase */
import { FunctionComponent } from "react";
import { Alert, Button, Checkbox, Divider, Stack, Text } from "@mantine/core";
import { useForm } from "@mantine/form";
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useRefTracksByEpisodeId,
useRefTracksByMovieId,
useSubtitleAction,
} from "@/apis/hooks";
import { GroupedSelector, Selector } from "@/components/inputs";
import { useModals, withModal } from "@/modules/modals";
import { task } from "@/modules/task";
import { syncMaxOffsetSecondsOptions } from "@/pages/Settings/Subtitles/options";
import { toPython } from "@/utilities";
import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Alert, Button, Checkbox, Divider, Stack, Text } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
import { GroupedSelector, Selector } from "../inputs";
const TaskName = "Syncing Subtitle";

View file

@ -1,12 +1,12 @@
import { FunctionComponent } from "react";
import { Button, Divider, Group, NumberInput, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useSubtitleAction } from "@/apis/hooks";
import { useModals, withModal } from "@/modules/modals";
import { task } from "@/modules/task";
import FormUtils from "@/utilities/form";
import { faMinus, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, Divider, Group, NumberInput, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
const TaskName = "Changing Time";

View file

@ -1,14 +1,14 @@
import { FunctionComponent, useMemo } from "react";
import { Alert, Button, Divider, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { isObject } from "lodash";
import { useSubtitleAction } from "@/apis/hooks";
import { Selector } from "@/components/inputs";
import { useModals, withModal } from "@/modules/modals";
import { task } from "@/modules/task";
import { useSelectorOptions } from "@/utilities";
import FormUtils from "@/utilities/form";
import { useEnabledLanguages } from "@/utilities/languages";
import { Alert, Button, Divider, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { isObject } from "lodash";
import { FunctionComponent, useMemo } from "react";
import { Selector } from "../inputs";
const TaskName = "Translating Subtitles";

View file

@ -1,7 +1,7 @@
import { render, screen } from "@/tests";
import { faStickyNote } from "@fortawesome/free-regular-svg-icons";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
import { render, screen } from "@/tests";
import Action from "./Action";
const testLabel = "Test Label";

View file

@ -1,15 +1,15 @@
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
import {
FontAwesomeIcon,
FontAwesomeIconProps,
} from "@fortawesome/react-fontawesome";
import { forwardRef } from "react";
import {
ActionIcon,
ActionIconProps,
Tooltip,
TooltipProps,
} from "@mantine/core";
import { forwardRef } from "react";
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
import {
FontAwesomeIcon,
FontAwesomeIconProps,
} from "@fortawesome/react-fontawesome";
export type ActionProps = MantineComp<ActionIconProps, "button"> & {
icon: IconDefinition;

View file

@ -1,6 +1,6 @@
import { render, screen } from "@/tests";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
import { render, screen } from "@/tests";
import ChipInput from "./ChipInput";
describe("ChipInput", () => {

View file

@ -1,12 +1,12 @@
import { FunctionComponent } from "react";
import { Group, Stack, Text } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import {
faArrowUp,
faFileCirclePlus,
faXmark,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Group, Stack, Text } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { FunctionComponent } from "react";
import styles from "./DropContent.module.scss";
export const DropContent: FunctionComponent = () => {

View file

@ -1,13 +1,13 @@
import { useFileSystem } from "@/apis/hooks";
import { faFolder } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FunctionComponent, useEffect, useMemo, useRef, useState } from "react";
import {
Autocomplete,
AutocompleteProps,
ComboboxItem,
OptionsFilter,
} from "@mantine/core";
import { FunctionComponent, useEffect, useMemo, useRef, useState } from "react";
import { faFolder } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useFileSystem } from "@/apis/hooks";
// TODO: use fortawesome icons
const backKey = "⏎ Back";

View file

@ -1,6 +1,6 @@
import { render, screen } from "@/tests";
import userEvent from "@testing-library/user-event";
import { describe, it, vitest } from "vitest";
import { render, screen } from "@/tests";
import { Selector, SelectorOption } from "./Selector";
const selectorName = "Test Selections";

View file

@ -1,4 +1,4 @@
import { LOG } from "@/utilities/console";
import { useCallback, useMemo, useRef } from "react";
import {
ComboboxItem,
ComboboxParsedItemGroup,
@ -8,7 +8,7 @@ import {
SelectProps,
} from "@mantine/core";
import { isNull, isUndefined } from "lodash";
import { useCallback, useMemo, useRef } from "react";
import { LOG } from "@/utilities/console";
export type SelectorOption<T> = Override<
{

View file

@ -1,23 +1,23 @@
/* eslint-disable camelcase */
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
import { Badge, Center, Text } from "@mantine/core";
import { faFileExcel, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useEpisodeAddBlacklist,
useEpisodeHistory,
useMovieAddBlacklist,
useMovieHistory,
} from "@/apis/hooks";
import { PageTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
import QueryOverlay from "@/components/async/QueryOverlay";
import { HistoryIcon } from "@/components/bazarr";
import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import TextPopover from "@/components/TextPopover";
import { withModal } from "@/modules/modals";
import { faFileExcel, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Badge, Center, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
import { PageTable } from "..";
import TextPopover from "../TextPopover";
import MutateAction from "../async/MutateAction";
import QueryOverlay from "../async/QueryOverlay";
import { HistoryIcon } from "../bazarr";
import Language from "../bazarr/Language";
interface MovieHistoryViewProps {
movie: Item.Movie;

View file

@ -1,12 +1,6 @@
import { withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
import { GetItemId } from "@/utilities";
import {
faCaretDown,
faDownload,
faInfoCircle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useCallback, useMemo, useState } from "react";
import { UseQueryResult } from "react-query";
import { Column } from "react-table";
import {
Alert,
Anchor,
@ -18,13 +12,19 @@ import {
Stack,
Text,
} from "@mantine/core";
import {
faCaretDown,
faDownload,
faInfoCircle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { isString } from "lodash";
import { useCallback, useMemo, useState } from "react";
import { UseQueryResult } from "react-query";
import { Column } from "react-table";
import { Action, PageTable } from "..";
import Language from "../bazarr/Language";
import StateIcon from "../StateIcon";
import { Action, PageTable } from "@/components";
import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import { withModal } from "@/modules/modals";
import { task, TaskGroup } from "@/modules/task";
import { GetItemId } from "@/utilities";
type SupportType = Item.Movie | Item.Episode;

View file

@ -1,12 +1,12 @@
import { FunctionComponent, useMemo, useState } from "react";
import { Column, useRowSelect } from "react-table";
import { Badge, Button, Divider, Group, Stack, Text } from "@mantine/core";
import Language from "@/components/bazarr/Language";
import SubtitleToolsMenu from "@/components/SubtitleToolsMenu";
import { SimpleTable } from "@/components/tables";
import { useCustomSelection } from "@/components/tables/plugins";
import { withModal } from "@/modules/modals";
import { isMovie } from "@/utilities";
import { Badge, Button, Divider, Group, Stack, Text } from "@mantine/core";
import { FunctionComponent, useMemo, useState } from "react";
import { Column, useRowSelect } from "react-table";
type SupportType = Item.Episode | Item.Movie;

View file

@ -1,8 +1,8 @@
import { useIsLoading } from "@/contexts";
import { usePageSize } from "@/utilities/storage";
import { Box, Skeleton, Table, Text } from "@mantine/core";
import { ReactNode, useMemo } from "react";
import { HeaderGroup, Row, TableInstance } from "react-table";
import { Box, Skeleton, Table, Text } from "@mantine/core";
import { useIsLoading } from "@/contexts";
import { usePageSize } from "@/utilities/storage";
import styles from "./BaseTable.module.scss";
export type BaseTableProps<T extends object> = TableInstance<T> & {

View file

@ -1,6 +1,3 @@
import { faChevronCircleRight } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Box, Text, Table } from "@mantine/core";
import {
Cell,
HeaderGroup,
@ -9,6 +6,9 @@ import {
useGroupBy,
useSortBy,
} from "react-table";
import { Box, Table, Text } from "@mantine/core";
import { faChevronCircleRight } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import SimpleTable, { SimpleTableProps } from "./SimpleTable";
function renderCell<T extends object = object>(cell: Cell<T>, row: Row<T>) {

View file

@ -1,6 +1,7 @@
import { useIsLoading } from "@/contexts";
import { Group, Pagination, Text } from "@mantine/core";
import { FunctionComponent, useEffect } from "react";
import { Group, Pagination, Text } from "@mantine/core";
import { useIsLoading } from "@/contexts";
interface Props {
count: number;
index: number;
@ -28,7 +29,7 @@ const PageControl: FunctionComponent<Props> = ({
}, [total, goto]);
return (
<Group p={16} justify="apart">
<Group p={16} justify="space-between">
<Text size="sm">
Show {start} to {end} of {total} entries
</Text>

View file

@ -1,11 +1,11 @@
import { ScrollToTop } from "@/utilities";
import { usePageSize } from "@/utilities/storage";
import { useEffect } from "react";
import { usePagination, useTable } from "react-table";
import { ScrollToTop } from "@/utilities";
import { usePageSize } from "@/utilities/storage";
import BaseTable from "./BaseTable";
import PageControl from "./PageControl";
import { SimpleTableProps } from "./SimpleTable";
import { useDefaultSettings } from "./plugins";
import { SimpleTableProps } from "./SimpleTable";
type Props<T extends object> = SimpleTableProps<T> & {
autoScroll?: boolean;

View file

@ -1,7 +1,7 @@
import { useEffect } from "react";
import { UsePaginationQueryResult } from "@/apis/queries/hooks";
import { LoadingProvider } from "@/contexts";
import { ScrollToTop } from "@/utilities";
import { useEffect } from "react";
import PageControl from "./PageControl";
import SimpleTable, { SimpleTableProps } from "./SimpleTable";

View file

@ -1,16 +1,16 @@
import { Checkbox as MantineCheckbox } from "@mantine/core";
import { forwardRef, useEffect, useRef } from "react";
import {
CellProps,
Column,
ColumnInstance,
ensurePluginOrder,
HeaderProps,
Hooks,
MetaBase,
TableInstance,
TableToggleCommonProps,
ensurePluginOrder,
} from "react-table";
import { Checkbox as MantineCheckbox } from "@mantine/core";
const pluginName = "useCustomSelection";

View file

@ -1,5 +1,5 @@
import { usePageSize } from "@/utilities/storage";
import { Hooks, TableOptions } from "react-table";
import { usePageSize } from "@/utilities/storage";
const pluginName = "useLocalSettings";

View file

@ -1,6 +1,3 @@
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, ButtonProps, Text } from "@mantine/core";
import {
ComponentProps,
FunctionComponent,
@ -8,6 +5,9 @@ import {
useCallback,
useState,
} from "react";
import { Button, ButtonProps, Text } from "@mantine/core";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
type ToolboxButtonProps = Omit<ButtonProps, "color" | "variant" | "leftIcon"> &
Omit<ComponentProps<"button">, "ref"> & {

View file

@ -1,9 +1,9 @@
.group {
@include light {
color: var(--mantine-color-gray-3);
background-color: var(--mantine-color-gray-3);
}
@include dark {
color: var(--mantine-color-dark-5);
background-color: var(--mantine-color-dark-5);
}
}

View file

@ -1,5 +1,5 @@
import { Group } from "@mantine/core";
import { FunctionComponent, PropsWithChildren } from "react";
import { Group } from "@mantine/core";
import ToolboxButton, { ToolboxMutateButton } from "./Button";
import styles from "./Toolbox.module.scss";
@ -10,7 +10,7 @@ declare type ToolboxComp = FunctionComponent<PropsWithChildren> & {
const Toolbox: ToolboxComp = ({ children }) => {
return (
<Group p={12} justify="apart" className={styles.group}>
<Group p={12} justify="space-between" className={styles.group}>
{children}
</Group>
);

View file

@ -1,7 +1,7 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Router } from "./Router";
import { AllProviders } from "./providers";
import { Router } from "./Router";
const container = document.getElementById("root");

View file

@ -1,8 +1,8 @@
import { FunctionComponent, PropsWithChildren, useMemo } from "react";
import {
ModalsProvider as MantineModalsProvider,
ModalsProviderProps as MantineModalsProviderProps,
} from "@mantine/modals";
import { FunctionComponent, PropsWithChildren, useMemo } from "react";
import { ModalComponent, StaticModals } from "./WithModal";
const DefaultModalProps: MantineModalsProviderProps["modalProps"] = {

View file

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/ban-types */
import { createContext, FunctionComponent } from "react";
import { ContextModalProps } from "@mantine/modals";
import { ModalSettings } from "@mantine/modals/lib/context";
import { createContext, FunctionComponent } from "react";
export type ModalComponent<P extends Record<string, unknown> = {}> =
FunctionComponent<ContextModalProps<P>> & {

View file

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/ban-types */
import { useCallback, useContext, useMemo } from "react";
import { useModals as useMantineModals } from "@mantine/modals";
import { ModalSettings } from "@mantine/modals/lib/context";
import { useCallback, useContext, useMemo } from "react";
import { ModalComponent, ModalIdContext } from "./WithModal";
export function useModals() {

View file

@ -1,6 +1,6 @@
import { useEffect } from "react";
import { LOG } from "@/utilities/console";
import Socketio from ".";
import { LOG } from "../../utilities/console";
export function useSocketIOReducer(reducer: SocketIO.Reducer) {
useEffect(() => {

View file

@ -1,8 +1,8 @@
import { debounce, forIn, remove, uniq } from "lodash";
import { onlineManager } from "react-query";
import { Socket, io } from "socket.io-client";
import { Environment, isDevEnv, isTestEnv } from "../../utilities";
import { ENSURE, GROUP, LOG } from "../../utilities/console";
import { debounce, forIn, remove, uniq } from "lodash";
import { io, Socket } from "socket.io-client";
import { Environment, isDevEnv, isTestEnv } from "@/utilities";
import { ENSURE, GROUP, LOG } from "@/utilities/console";
import { createDefaultReducer } from "./reducer";
class SocketIOClient {

View file

@ -1,9 +1,9 @@
import { cleanNotifications, showNotification } from "@mantine/notifications";
import queryClient from "@/apis/queries";
import { QueryKeys } from "@/apis/queries/keys";
import { notification, task } from "@/modules/task";
import { LOG } from "@/utilities/console";
import { setCriticalError, setOnlineStatus } from "@/utilities/event";
import { cleanNotifications, showNotification } from "@mantine/notifications";
import { notification, task } from "../task";
export function createDefaultReducer(): SocketIO.Reducer[] {
return [

View file

@ -1,10 +1,10 @@
import { LOG } from "@/utilities/console";
import {
hideNotification,
showNotification,
updateNotification,
} from "@mantine/notifications";
import { uniqueId } from "lodash";
import { LOG } from "@/utilities/console";
import { notification } from "./notification";
class TaskDispatcher {

View file

@ -1,5 +1,5 @@
import { render, screen } from "@/tests";
import { describe, it } from "vitest";
import { render, screen } from "@/tests";
import Authentication from "./Authentication";
describe("Authentication", () => {

View file

@ -1,5 +1,4 @@
import { useSystem } from "@/apis/hooks";
import { Environment } from "@/utilities";
import { FunctionComponent } from "react";
import {
Avatar,
Button,
@ -11,7 +10,8 @@ import {
TextInput,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { FunctionComponent } from "react";
import { useSystem } from "@/apis/hooks";
import { Environment } from "@/utilities";
const Authentication: FunctionComponent = () => {
const { login } = useSystem();

View file

@ -1,13 +1,13 @@
import { FunctionComponent } from "react";
import { Container, Stack } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import {
useMovieBlacklist,
useMovieDeleteBlacklist,
} from "@/apis/hooks/movies";
import { Toolbox } from "@/components";
import { QueryOverlay } from "@/components/async";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Container, Stack } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { FunctionComponent } from "react";
import Table from "./table";
const BlacklistMoviesView: FunctionComponent = () => {

View file

@ -1,13 +1,13 @@
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
import { Anchor, Text } from "@mantine/core";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { useMovieDeleteBlacklist } from "@/apis/hooks";
import { PageTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
import Language from "@/components/bazarr/Language";
import TextPopover from "@/components/TextPopover";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
interface Props {
blacklist: readonly Blacklist.Movie[];

View file

@ -1,10 +1,10 @@
import { FunctionComponent } from "react";
import { Container, Stack } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { useEpisodeBlacklist, useEpisodeDeleteBlacklist } from "@/apis/hooks";
import { Toolbox } from "@/components";
import { QueryOverlay } from "@/components/async";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Container, Stack } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { FunctionComponent } from "react";
import Table from "./table";
const BlacklistSeriesView: FunctionComponent = () => {

View file

@ -1,13 +1,13 @@
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
import { Anchor, Text } from "@mantine/core";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { useEpisodeDeleteBlacklist } from "@/apis/hooks";
import { PageTable } from "@/components";
import MutateAction from "@/components/async/MutateAction";
import Language from "@/components/bazarr/Language";
import TextPopover from "@/components/TextPopover";
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { Anchor, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
interface Props {
blacklist: readonly Blacklist.Episode[];

View file

@ -1,9 +1,9 @@
import { FunctionComponent, useMemo, useState } from "react";
import { Badge, MantineColor, Tooltip } from "@mantine/core";
import { useEpisodeSubtitleModification } from "@/apis/hooks";
import Language from "@/components/bazarr/Language";
import SubtitleToolsMenu from "@/components/SubtitleToolsMenu";
import { task, TaskGroup } from "@/modules/task";
import { Badge, MantineColor, Tooltip } from "@mantine/core";
import { FunctionComponent, useMemo, useState } from "react";
interface Props {
seriesId: number;
@ -24,13 +24,13 @@ export const Subtitle: FunctionComponent<Props> = ({
const disabled = subtitle.path === null;
const color: MantineColor | undefined = useMemo(() => {
const variant: MantineColor | undefined = useMemo(() => {
if (opened && !disabled) {
return "cyan";
return "highlight";
} else if (missing) {
return "yellow";
return "warning";
} else if (disabled) {
return "gray";
return "disabled";
}
}, [disabled, missing, opened]);
@ -50,7 +50,7 @@ export const Subtitle: FunctionComponent<Props> = ({
}, [episodeId, subtitle.code2, subtitle.path]);
const ctx = (
<Badge color={color}>
<Badge variant={variant}>
<Language.Text value={subtitle} long={false}></Language.Text>
</Badge>
);

View file

@ -1,4 +1,26 @@
import { RouterNames } from "@/Router/RouterNames";
import {
FunctionComponent,
useCallback,
useMemo,
useRef,
useState,
} from "react";
import { Navigate, useParams } from "react-router-dom";
import { Container, Group, Stack } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useDocumentTitle } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import {
faAdjust,
faBriefcase,
faCircleChevronDown,
faCircleChevronRight,
faCloudUploadAlt,
faHdd,
faSearch,
faSync,
faWrench,
} from "@fortawesome/free-solid-svg-icons";
import {
useEpisodesBySeriesId,
useIsAnyActionRunning,
@ -12,32 +34,10 @@ import { ItemEditModal } from "@/components/forms/ItemEditForm";
import { SeriesUploadModal } from "@/components/forms/SeriesUploadForm";
import { SubtitleToolsModal } from "@/components/modals";
import { useModals } from "@/modules/modals";
import { TaskGroup, notification, task } from "@/modules/task";
import { notification, task, TaskGroup } from "@/modules/task";
import ItemOverview from "@/pages/views/ItemOverview";
import { RouterNames } from "@/Router/RouterNames";
import { useLanguageProfileBy } from "@/utilities/languages";
import {
faAdjust,
faBriefcase,
faCircleChevronDown,
faCircleChevronRight,
faCloudUploadAlt,
faHdd,
faSearch,
faSync,
faWrench,
} from "@fortawesome/free-solid-svg-icons";
import { Container, Group, Stack } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useDocumentTitle } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import {
FunctionComponent,
useCallback,
useMemo,
useRef,
useState,
} from "react";
import { Navigate, useParams } from "react-router-dom";
import Table from "./table";
const SeriesEpisodesView: FunctionComponent = () => {

View file

@ -1,21 +1,3 @@
import { useDownloadEpisodeSubtitles, useEpisodesProvider } from "@/apis/hooks";
import { useShowOnlyDesired } from "@/apis/hooks/site";
import { Action, GroupTable } from "@/components";
import TextPopover from "@/components/TextPopover";
import { AudioList } from "@/components/bazarr";
import { EpisodeHistoryModal } from "@/components/modals";
import { EpisodeSearchModal } from "@/components/modals/ManualSearchModal";
import { useModals } from "@/modules/modals";
import { BuildKey, filterSubtitleBy } from "@/utilities";
import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import {
faBookmark,
faHistory,
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Group, Text } from "@mantine/core";
import {
FunctionComponent,
useCallback,
@ -24,6 +6,24 @@ import {
useRef,
} from "react";
import { Column, TableInstance } from "react-table";
import { Group, Text } from "@mantine/core";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import {
faBookmark,
faHistory,
faUser,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useDownloadEpisodeSubtitles, useEpisodesProvider } from "@/apis/hooks";
import { useShowOnlyDesired } from "@/apis/hooks/site";
import { Action, GroupTable } from "@/components";
import { AudioList } from "@/components/bazarr";
import { EpisodeHistoryModal } from "@/components/modals";
import { EpisodeSearchModal } from "@/components/modals/ManualSearchModal";
import TextPopover from "@/components/TextPopover";
import { useModals } from "@/modules/modals";
import { BuildKey, filterSubtitleBy } from "@/utilities";
import { useProfileItemsToLanguages } from "@/utilities/languages";
import { Subtitle } from "./components";
interface Props {
@ -169,7 +169,7 @@ const Table: FunctionComponent<Props> = ({
<Action
label="Manual Search"
disabled={disabled}
color="dark"
variant="dark"
onClick={() => {
modals.openContextModal(EpisodeSearchModal, {
item: row.original,
@ -182,7 +182,7 @@ const Table: FunctionComponent<Props> = ({
<Action
label="History"
disabled={disabled}
color="dark"
variant="dark"
onClick={() => {
modals.openContextModal(
EpisodeHistoryModal,

View file

@ -1,4 +1,14 @@
/* eslint-disable camelcase */
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
import { Anchor, Badge, Text } from "@mantine/core";
import {
faFileExcel,
faInfoCircle,
faRecycle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMovieAddBlacklist, useMovieHistoryPagination } from "@/apis/hooks";
import { MutateAction } from "@/components/async";
import { HistoryIcon } from "@/components/bazarr";
@ -6,16 +16,6 @@ import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import TextPopover from "@/components/TextPopover";
import HistoryView from "@/pages/views/HistoryView";
import {
faFileExcel,
faInfoCircle,
faRecycle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Anchor, Badge, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
const MoviesHistoryView: FunctionComponent = () => {
const columns: Column<History.Movie>[] = useMemo<Column<History.Movie>[]>(

View file

@ -1,4 +1,14 @@
/* eslint-disable camelcase */
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
import { Anchor, Badge, Text } from "@mantine/core";
import {
faFileExcel,
faInfoCircle,
faRecycle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
useEpisodeAddBlacklist,
useEpisodeHistoryPagination,
@ -9,16 +19,6 @@ import Language from "@/components/bazarr/Language";
import StateIcon from "@/components/StateIcon";
import TextPopover from "@/components/TextPopover";
import HistoryView from "@/pages/views/HistoryView";
import {
faFileExcel,
faInfoCircle,
faRecycle,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Anchor, Badge, Text } from "@mantine/core";
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
const SeriesHistoryView: FunctionComponent = () => {
const columns: Column<History.Episode>[] = useMemo<Column<History.Episode>[]>(

View file

@ -1,15 +1,7 @@
import {
useHistoryStats,
useLanguages,
useSystemProviders,
} from "@/apis/hooks";
import { Selector, Toolbox } from "@/components";
import { QueryOverlay } from "@/components/async";
import { useSelectorOptions } from "@/utilities";
import { FunctionComponent, useMemo, useState } from "react";
import { Box, Container, SimpleGrid, useMantineTheme } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { merge } from "lodash";
import { FunctionComponent, useMemo, useState } from "react";
import {
Bar,
BarChart,
@ -20,6 +12,14 @@ import {
XAxis,
YAxis,
} from "recharts";
import {
useHistoryStats,
useLanguages,
useSystemProviders,
} from "@/apis/hooks";
import { Selector, Toolbox } from "@/components";
import { QueryOverlay } from "@/components/async";
import { useSelectorOptions } from "@/utilities";
import { actionOptions, timeFrameOptions } from "./options";
import styles from "./HistoryStats.module.scss";

View file

@ -1,7 +1,7 @@
import { renderTest, RenderTestCase } from "@/tests/render";
import HistoryStats from "./Statistics/HistoryStats";
import MoviesHistoryView from "./Movies";
import SeriesHistoryView from "./Series";
import HistoryStats from "./Statistics/HistoryStats";
const cases: RenderTestCase[] = [
{

View file

@ -1,4 +1,21 @@
import { RouterNames } from "@/Router/RouterNames";
import { FunctionComponent, useCallback, useRef } from "react";
import { Navigate, useParams } from "react-router-dom";
import { Container, Group, Menu, Stack } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useDocumentTitle } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import {
faCloudUploadAlt,
faEllipsis,
faHistory,
faSearch,
faSync,
faToolbox,
faUser,
faWrench,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { isNumber } from "lodash";
import {
useDownloadMovieSubtitles,
useIsMovieActionRunning,
@ -16,27 +33,10 @@ import { MovieUploadModal } from "@/components/forms/MovieUploadForm";
import { MovieHistoryModal, SubtitleToolsModal } from "@/components/modals";
import { MovieSearchModal } from "@/components/modals/ManualSearchModal";
import { useModals } from "@/modules/modals";
import { TaskGroup, notification, task } from "@/modules/task";
import { notification, task, TaskGroup } from "@/modules/task";
import ItemOverview from "@/pages/views/ItemOverview";
import { RouterNames } from "@/Router/RouterNames";
import { useLanguageProfileBy } from "@/utilities/languages";
import {
faCloudUploadAlt,
faEllipsis,
faHistory,
faSearch,
faSync,
faToolbox,
faUser,
faWrench,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Container, Group, Menu, Stack } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useDocumentTitle } from "@mantine/hooks";
import { showNotification } from "@mantine/notifications";
import { isNumber } from "lodash";
import { FunctionComponent, useCallback, useRef } from "react";
import { Navigate, useParams } from "react-router-dom";
import Table from "./table";
const MovieDetailView: FunctionComponent = () => {
@ -198,7 +198,6 @@ const MovieDetailView: FunctionComponent = () => {
<Menu.Target>
<Action
label="More Actions"
color="dark"
icon={faEllipsis}
disabled={hasTask}
/>

View file

@ -1,3 +1,8 @@
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
import { Badge, Text, TextProps } from "@mantine/core";
import { faEllipsis, faSearch } from "@fortawesome/free-solid-svg-icons";
import { isString } from "lodash";
import { useMovieSubtitleModification } from "@/apis/hooks";
import { useShowOnlyDesired } from "@/apis/hooks/site";
import { Action, SimpleTable } from "@/components";
@ -6,11 +11,6 @@ import SubtitleToolsMenu from "@/components/SubtitleToolsMenu";
import { task, TaskGroup } from "@/modules/task";
import { filterSubtitleBy } from "@/utilities";
import { useProfileItemsToLanguages } from "@/utilities/languages";
import { faEllipsis, faSearch } from "@fortawesome/free-solid-svg-icons";
import { Badge, Text, TextProps } from "@mantine/core";
import { isString } from "lodash";
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
const missingText = "Missing Subtitles";
@ -161,7 +161,7 @@ const Table: FunctionComponent<Props> = ({ movie, profile, disabled }) => {
<Action
label="Subtitle Actions"
disabled={isSubtitleTrack(path)}
color="dark"
variant="dark"
icon={faEllipsis}
></Action>
</SubtitleToolsMenu>

View file

@ -1,11 +1,11 @@
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
import { useDocumentTitle } from "@mantine/hooks";
import { useMovieModification, useMovies } from "@/apis/hooks";
import { QueryOverlay } from "@/components/async";
import { AudioList } from "@/components/bazarr";
import LanguageProfileName from "@/components/bazarr/LanguageProfile";
import MassEditor from "@/pages/views/MassEditor";
import { useDocumentTitle } from "@mantine/hooks";
import { FunctionComponent, useMemo } from "react";
import { Column } from "react-table";
const MovieMassEditor: FunctionComponent = () => {
const query = useMovies();

View file

@ -1,3 +1,11 @@
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
import { Anchor, Badge, Container } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import { faBookmark, faWrench } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMovieModification, useMoviesPagination } from "@/apis/hooks";
import { Action } from "@/components";
import { AudioList } from "@/components/bazarr";
@ -7,14 +15,6 @@ import { ItemEditModal } from "@/components/forms/ItemEditForm";
import { useModals } from "@/modules/modals";
import ItemView from "@/pages/views/ItemView";
import { BuildKey } from "@/utilities";
import { faBookmark as farBookmark } from "@fortawesome/free-regular-svg-icons";
import { faBookmark, faWrench } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Anchor, Badge, Container } from "@mantine/core";
import { useDocumentTitle } from "@mantine/hooks";
import { FunctionComponent, useMemo } from "react";
import { Link } from "react-router-dom";
import { Column } from "react-table";
const MovieView: FunctionComponent = () => {
const query = useMoviesPagination();

Some files were not shown because too many files have changed in this diff Show more