+ Subdirectory support
This commit is contained in:
parent
bd604035a5
commit
4df949b770
|
@ -63,6 +63,7 @@
|
|||
"build": "react-app-rewired build",
|
||||
"test": "react-app-rewired test"
|
||||
},
|
||||
"homepage": "./",
|
||||
"config-overrides-path": "scripts/config-overrides",
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
@ -4,17 +4,22 @@ IndexIgnore *
|
|||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
# RewriteBase /
|
||||
|
||||
# Force trailing slash
|
||||
RewriteCond %{REQUEST_URI} /+[^\.]+$
|
||||
RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [R=301,L]
|
||||
|
||||
# Rewrite jpg/png/gif/webp file access to uploads folder
|
||||
# Only rewrite in base directory
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{DOCUMENT_ROOT}/uploads/$1.$2 -f
|
||||
RewriteRule ^(.+)\.(jpg|png|gif|webp|mp4)$ uploads/$1.$2 [L]
|
||||
RewriteRule ^([^\/]+)\.(jpg|png|gif|webp|mp4)$ uploads/$1.$2 [L]
|
||||
|
||||
# Rewrite requests going into an unknown /static/ folder
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.+)\/(static)\/(.+) static/$3 [L]
|
||||
|
||||
# Don't rewrite files or directories
|
||||
RewriteRule ^index.php$ - [L]
|
||||
|
@ -22,7 +27,7 @@ IndexIgnore *
|
|||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
# Rewrite everything else to index.php to allow html5 state links
|
||||
RewriteRule . /index.php [L]
|
||||
RewriteRule . index.php [L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
<?php
|
||||
require_once "./protected/output.inc.php";
|
||||
session_start();
|
||||
|
||||
/**
|
||||
* Determines in which directory Shadis is located in
|
||||
*/
|
||||
$base_directory = dirname($_SERVER['SCRIPT_NAME']);
|
||||
$homepage = url_origin($_SERVER) . $base_directory;
|
||||
|
||||
$uri_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$segments = explode('/', trim($uri_path, '/'));
|
||||
$file_data = null;
|
||||
|
@ -9,7 +16,6 @@ $title = "Shadis";
|
|||
if (!empty($segments[0])) {
|
||||
if (strlen($segments[0]) === 8) {
|
||||
require_once "./protected/db.inc.php";
|
||||
require_once "./protected/output.inc.php";
|
||||
$file_data = $db->request_file($segments[0]);
|
||||
$title = ($file_data["title"] !== "" ? ($file_data["title"] . " - ") : "") . $file_data["id"] . " - Shadis";
|
||||
}
|
||||
|
@ -20,20 +26,23 @@ if (!empty($segments[0])) {
|
|||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/static/media/favicon.ico" />
|
||||
<link rel="icon" href="<?php echo $homepage . "/static/media/favicon.ico"; ?>" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta content="noindex" name="robots">
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/static/media/logo192.png" />
|
||||
<link rel="apple-touch-icon" href="<?php echo $homepage . "/static/media/logo192.png"; ?>" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>Shadis</title>
|
||||
<link rel="manifest" href="<?php echo $homepage . "/manifest.json"; ?>" />
|
||||
<title><?php echo $title; ?></title>
|
||||
<meta name="title" content="<?php echo $title; ?>">
|
||||
<meta name="description" content="Share and host your favourite screenshots and screencaptures on your own server!">
|
||||
<?php
|
||||
echo '<script>var baseDirectory = ';
|
||||
echo json_encode($base_directory);
|
||||
echo '</script>';
|
||||
if (isset($_SESSION["u_id"])) {
|
||||
$user_data = array("username" => $_SESSION["u_name"]);
|
||||
echo '<script>var userData = ';
|
||||
|
@ -42,8 +51,7 @@ if (!empty($segments[0])) {
|
|||
}
|
||||
if (!is_null($file_data)) :
|
||||
$file_data["fromServer"] = true;
|
||||
$origin_url = url_origin($_SERVER);
|
||||
$file_url = $origin_url . "/" . $file_data["id"] . "." . $file_data["extension"];
|
||||
$file_url = $homepage . "/" . $file_data["id"] . "." . $file_data["extension"];
|
||||
|
||||
echo '<script>var fileData = ';
|
||||
echo json_encode($file_data);
|
||||
|
@ -77,7 +85,7 @@ if (!empty($segments[0])) {
|
|||
<meta property="og:image" content="<?php echo $file_url; ?>">
|
||||
<meta property="og:image:width" content="<?php echo $file_data["width"]; ?>">
|
||||
<meta property="og:image:height" content="<?php echo $file_data["height"]; ?>">
|
||||
<meta property="og:url" content="<?php echo $origin_url . "/" . $file_data["id"] . "/"; ?>">
|
||||
<meta property="og:url" content="<?php echo $homepage . "/" . $file_data["id"] . "/"; ?>">
|
||||
|
||||
<!-- Twitter Metadata -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
|
|
@ -37,14 +37,6 @@ define("UPLOAD_TOKEN", "your_secret_upload_token_here");
|
|||
*/
|
||||
$table_prefix = "shadis_";
|
||||
|
||||
/**
|
||||
* Base directory of Shadis
|
||||
*
|
||||
* If you put Shadis in a location other than root,
|
||||
* modify this variable to the specific subdirectory.
|
||||
*/
|
||||
$base_directory = "/";
|
||||
|
||||
/**
|
||||
* Path to the upload directory of Shadis
|
||||
*
|
||||
|
|
|
@ -15,6 +15,32 @@ process.on("unhandledRejection", err => {
|
|||
throw err;
|
||||
});
|
||||
|
||||
/**
|
||||
* Rewire publicPath generator
|
||||
*
|
||||
* .htaccess reroutes requests of any undefined `static` directory to the `static`
|
||||
* folder found in project root. Thus, we can make `publicPath` a relative value.
|
||||
*/
|
||||
require("react-dev-utils/getPublicUrlOrPath");
|
||||
require.cache[require.resolve("react-dev-utils/getPublicUrlOrPath")].exports = (
|
||||
isEnvDevelopment,
|
||||
homepage
|
||||
) => {
|
||||
const { URL } = require("url");
|
||||
const stubDomain = "https://create-react-app.dev";
|
||||
|
||||
if (homepage) {
|
||||
// strip last slash if exists
|
||||
homepage = homepage.endsWith("/") ? homepage : homepage + "/";
|
||||
|
||||
// validate if `homepage` is a URL or path like and use just pathname
|
||||
const validHomepagePathname = new URL(homepage, stubDomain).pathname;
|
||||
return homepage.startsWith(".") ? homepage : validHomepagePathname;
|
||||
}
|
||||
|
||||
return "/";
|
||||
};
|
||||
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("react-scripts/config/paths");
|
||||
const path = require("path");
|
||||
|
@ -27,8 +53,8 @@ const chalk = require("chalk");
|
|||
/**
|
||||
* Rewire compilation success screen
|
||||
*/
|
||||
const reactDevUtils = rewire("react-dev-utils/WebpackDevServerUtils");
|
||||
reactDevUtils.__set__("printInstructions", (appName, urls, useYarn) => {
|
||||
const webpackDevUtils = rewire("react-dev-utils/WebpackDevServerUtils");
|
||||
webpackDevUtils.__set__("printInstructions", (appName, urls, useYarn) => {
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.cyan("ℹ️ The development bundle was output to " + chalk.bold("dist"))
|
||||
|
@ -133,7 +159,7 @@ const urls = {
|
|||
};
|
||||
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = reactDevUtils.createCompiler({
|
||||
const compiler = webpackDevUtils.createCompiler({
|
||||
appName,
|
||||
config,
|
||||
devSocket,
|
||||
|
|
|
@ -11,6 +11,7 @@ interface Window {
|
|||
* @memberof Window
|
||||
*/
|
||||
userData?: { username: string };
|
||||
|
||||
/**
|
||||
* THe backend sets a global "fileData" object
|
||||
* which provides everything necessary
|
||||
|
@ -33,6 +34,11 @@ interface Window {
|
|||
fromServer?: boolean;
|
||||
has_gif?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The project's root path, defined by the server
|
||||
*/
|
||||
baseDirectory?: string;
|
||||
}
|
||||
|
||||
declare module "react-resize-aware";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { FileViewProps, FileData } from "../FileView/FileView.props";
|
|||
import { FullscreenLoader } from "../Loader";
|
||||
import { RouteChildrenProps } from "react-router-dom";
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import axios from "../_interceptedAxios";
|
||||
import axios, { getApiPath } from "../_interceptedAxios";
|
||||
import { AnimatePresence, AnimateSharedLayout } from "framer-motion";
|
||||
import { isLoggedIn, toast, DesignToolkitProvider } from "../_DesignSystem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
@ -58,7 +58,7 @@ const useFilePrefetcher = (id: string, history: History<{}>) => {
|
|||
useEffect(() => {
|
||||
const fetchFileData = async (id: string) => {
|
||||
try {
|
||||
const res = await axios.get(window.location.origin + "/api/get.php", {
|
||||
const res = await axios.get(getApiPath("get"), {
|
||||
params: { id },
|
||||
});
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ const styles: ComponentStyles<AppContainerClassNameContract, DesignSystem> = {
|
|||
|
||||
const AppContainer: React.ComponentType<AppContainerProps> = ({ managedClasses }) => (
|
||||
<div className={managedClasses.container}>
|
||||
<Router>
|
||||
<Router basename={window.baseDirectory}>
|
||||
<Suspense fallback={null}>
|
||||
<Route path={["/:id", "/"]} component={AnimatedRoutes} />
|
||||
</Suspense>
|
||||
|
|
|
@ -4,7 +4,7 @@ import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
|
|||
import { DashboardClassNameContract, DashboardProps } from "./Dashboard.props";
|
||||
import { Header, toast } from "../_DesignSystem";
|
||||
import { withDropzone } from "../FullscreenDropzone/FullscreenDropzone";
|
||||
import axios from "../_interceptedAxios";
|
||||
import axios, { getApiPath } from "../_interceptedAxios";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ListDataItem, DashboardListProps } from "../DashboardList/DashboardList.props";
|
||||
import { FullscreenLoader } from "../Loader";
|
||||
|
@ -43,7 +43,7 @@ const Dashboard: React.FC<DashboardProps> = props => {
|
|||
useEffect(() => {
|
||||
const updateFileList = async () => {
|
||||
try {
|
||||
const res = await axios.get(window.location.origin + "/api/getAll.php");
|
||||
const res = await axios.get(getApiPath("getAll"));
|
||||
setListData(res.data);
|
||||
} catch (err) {
|
||||
toast.error(t("error.listGeneric") + ":", t(err.i18n, err.message));
|
||||
|
@ -56,7 +56,7 @@ const Dashboard: React.FC<DashboardProps> = props => {
|
|||
|
||||
const onDeleteSelected = async (selection: string[]) => {
|
||||
try {
|
||||
await axios.post(window.location.origin + "/api/edit.php", {
|
||||
await axios.post(getApiPath("edit"), {
|
||||
selection,
|
||||
action: "delete",
|
||||
});
|
||||
|
|
|
@ -27,6 +27,7 @@ import { classNames } from "@microsoft/fast-web-utilities";
|
|||
import { motion } from "framer-motion";
|
||||
import { Link } from "react-router-dom";
|
||||
import { FaPlayCircle } from "react-icons/fa";
|
||||
import { basePath } from "../../_interceptedAxios";
|
||||
|
||||
const styles: ComponentStyles<DashboardListCellClassNameContract, DesignSystem> = {
|
||||
dashboardListCell: {
|
||||
|
@ -230,7 +231,7 @@ const CellRenderer: React.FC<DashboardListCellProps> = props => {
|
|||
<Link to={`/${id}/`} onClick={shouldExecuteclick}>
|
||||
<img
|
||||
className={props.managedClasses.dashboardListCell_image}
|
||||
src={`${window.location.origin}/${id}.thumb.jpg`}
|
||||
src={`${basePath}/${id}.thumb.jpg`}
|
||||
alt={!title || title === "untitled" ? t("untitled") : title}
|
||||
onError={onImageError}
|
||||
onLoad={onImageLoaded}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { FVSidebarFooterProps } from "./FVSidebarFooter.props";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import axios from "../../../_interceptedAxios";
|
||||
import axios, { getApiPath } from "../../../_interceptedAxios";
|
||||
import { Button, ButtonAppearance, toast } from "../../../_DesignSystem";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
|
||||
|
@ -17,7 +17,7 @@ const FVSidebarDeleteButton: React.ComponentType<FVSidebarFooterProps> = ({
|
|||
|
||||
const onDelete = async () => {
|
||||
try {
|
||||
await axios.post(window.location.origin + "/api/edit.php", {
|
||||
await axios.post(getApiPath("edit"), {
|
||||
selection: fileData.id,
|
||||
action: "delete",
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
ProgressClassNameContract,
|
||||
} from "@microsoft/fast-components-react-msft";
|
||||
import { FaCheck, FaExclamationTriangle } from "react-icons/fa";
|
||||
import axios from "../../../_interceptedAxios";
|
||||
import axios, { getApiPath } from "../../../_interceptedAxios";
|
||||
import { toast } from "../../../_DesignSystem";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SidebarData } from "./FVSidebarContext";
|
||||
|
@ -68,7 +68,7 @@ const FVSidebarDescEditor: React.ComponentType<FVSidebarDescEditorProps> = ({
|
|||
const deferredLoading = setTimeout(() => setLoadingState("loading"), 500);
|
||||
|
||||
try {
|
||||
await axios.post(window.location.origin + "/api/edit.php", {
|
||||
await axios.post(getApiPath("edit"), {
|
||||
selection: fileData.id,
|
||||
value,
|
||||
action: "editTitle",
|
||||
|
|
|
@ -19,6 +19,7 @@ import { designSystemContext } from "@microsoft/fast-jss-manager-react/dist/cont
|
|||
import loadable from "@loadable/component";
|
||||
import { IconType } from "react-icons/lib";
|
||||
import FVSidebarConvertButton from "./FVSidebarConvertButton";
|
||||
import { basePath } from "../../../_interceptedAxios";
|
||||
|
||||
const FVSidebarDeleteButton = loadable(() => import("./FVSidebarDeleteButton"));
|
||||
|
||||
|
@ -143,7 +144,7 @@ const FVSidebarFooter: React.ComponentType<FVSidebarFooterProps> = ({
|
|||
<Button
|
||||
appearance={ButtonAppearance.stealth}
|
||||
icon={FaDownload}
|
||||
href={`${window.location.origin}/${fileData.id}.${fileData.extension}`}
|
||||
href={`${basePath}/${fileData.id}.${fileData.extension}`}
|
||||
target="_blank"
|
||||
download
|
||||
>
|
||||
|
@ -153,7 +154,7 @@ const FVSidebarFooter: React.ComponentType<FVSidebarFooterProps> = ({
|
|||
jssStyleSheet={fileData.has_gif ? overlapIconButtonStyles : null}
|
||||
appearance={ButtonAppearance.stealth}
|
||||
icon={fileData.has_gif ? OverlapIcon(FaVideo) : FaLink}
|
||||
href={`${window.location.origin}/${fileData.id}.${fileData.extension}`}
|
||||
href={`${basePath}/${fileData.id}.${fileData.extension}`}
|
||||
target="_blank"
|
||||
>
|
||||
{fileData.has_gif ? t("sourceVideo") : t("source")}
|
||||
|
@ -163,7 +164,7 @@ const FVSidebarFooter: React.ComponentType<FVSidebarFooterProps> = ({
|
|||
jssStyleSheet={overlapIconButtonStyles}
|
||||
appearance={ButtonAppearance.stealth}
|
||||
icon={OverlapIcon(FaImage)}
|
||||
href={`${window.location.origin}/${fileData.id}.gif`}
|
||||
href={`${basePath}/${fileData.id}.gif`}
|
||||
target="_blank"
|
||||
>
|
||||
{t("sourceGif")}
|
||||
|
|
|
@ -23,6 +23,7 @@ import ImageViewerSlider from "./ImageViewerSlider";
|
|||
import { TweenProps, spring } from "popmotion";
|
||||
import { SidebarData, SidebarEventEmitter } from "../FVSidebar/FVSidebarContext";
|
||||
import { ThumbnailContext, ssrContainer } from "../ThumbnailViewer/ThumbnailViewer";
|
||||
import { basePath } from "../../../_interceptedAxios";
|
||||
|
||||
const applyCenteredAbsolute: CSSRules<DesignSystem> = {
|
||||
position: "absolute",
|
||||
|
@ -123,7 +124,7 @@ const ImageViewer: React.ComponentType<ImageViewerProps> = ({
|
|||
|
||||
addEntryFinishListener(() => {
|
||||
image.addEventListener("load", listener);
|
||||
image.src = `${window.location.origin}/${id}.${extension}`;
|
||||
image.src = `${basePath}/${id}.${extension}`;
|
||||
|
||||
if (fromServer) {
|
||||
if (image.complete) listener();
|
||||
|
|
|
@ -9,6 +9,7 @@ import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
|
|||
import { isLoggedIn, headerHeight, useScaleFactor } from "../../../_DesignSystem";
|
||||
import { motion } from "framer-motion";
|
||||
import { useViewportDimensions } from "../ImageViewer/useViewportDimensions";
|
||||
import { basePath } from "../../../_interceptedAxios";
|
||||
|
||||
const styles: ComponentStyles<ThumbnailViewerClassNameContract, DesignSystem> = {
|
||||
viewer: {
|
||||
|
@ -172,7 +173,7 @@ const ThumbnailViewer: React.ComponentType<ThumbnailViewerProps> = ({
|
|||
onAnimationStart={onMagicAnimStart}
|
||||
onAnimationComplete={onMagicAnimEnd}
|
||||
alt={title}
|
||||
src={`${window.location.origin}/${id}.thumb.jpg`}
|
||||
src={`${basePath}/${id}.thumb.jpg`}
|
||||
style={{
|
||||
width: defaultWidth,
|
||||
height: defaultHeight,
|
||||
|
|
|
@ -8,6 +8,7 @@ import { SidebarData } from "../FVSidebar/FVSidebarContext";
|
|||
import { useTranslation } from "react-i18next";
|
||||
import { TabPanel } from "../../../_DesignSystem/Tabs/TabViewer/TabViewer.props";
|
||||
import tabEventEmitter from "../../../_DesignSystem/Tabs/TabEvents";
|
||||
import { basePath } from "../../../_interceptedAxios";
|
||||
|
||||
const styles: ComponentStyles<VideoViewerClassNameContract, DesignSystem> = {
|
||||
videoViewer: {
|
||||
|
@ -80,7 +81,7 @@ const VideoViewer: React.ComponentType<VideoViewerProps> = ({
|
|||
|
||||
addEntryFinishListener(() => {
|
||||
videoEl.addEventListener("canplaythrough", listener);
|
||||
videoEl.src = `${window.location.origin}/${id}.${extension}`;
|
||||
videoEl.src = `${basePath}/${id}.${extension}`;
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
@ -130,7 +131,7 @@ const VideoViewer: React.ComponentType<VideoViewerProps> = ({
|
|||
toastId = toast.info(t("gif.load"), "", { progress: 0 });
|
||||
}, 500) as any;
|
||||
|
||||
imgEl.src = `${window.location.origin}/${id}.gif`;
|
||||
imgEl.src = `${basePath}/${id}.gif`;
|
||||
}
|
||||
}, [id, t]);
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from "@microsoft/fast-components-styles-msft";
|
||||
import { parseColorHexRGBA } from "@microsoft/fast-colors";
|
||||
import { ProgressIcon, getScaleFactorByConstraints } from "../../_DesignSystem";
|
||||
import axios from "../../_interceptedAxios";
|
||||
import axios, { getApiPath } from "../../_interceptedAxios";
|
||||
|
||||
const DropzoneUploadStyles: ComponentStyles<
|
||||
DropzoneUploadClassNameContract,
|
||||
|
@ -194,11 +194,9 @@ const DropzoneUpload: React.ComponentType<DropzoneUploadProps> = React.memo(
|
|||
setUploadState(true);
|
||||
|
||||
try {
|
||||
const res = await axios.post(
|
||||
window.location.origin + "/api/upload.php",
|
||||
formData,
|
||||
{ onUploadProgress }
|
||||
);
|
||||
const res = await axios.post(getApiPath("upload"), formData, {
|
||||
onUploadProgress,
|
||||
});
|
||||
setResData(res.data);
|
||||
} catch (err) {
|
||||
console.log("An error happened!\n", err);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useCallback, useRef, useState } from "react";
|
||||
import { toast } from "../../_DesignSystem";
|
||||
import gifJS from "gif.js";
|
||||
import axios from "../../_interceptedAxios";
|
||||
import axios, { getApiPath, basePath } from "../../_interceptedAxios";
|
||||
import gifGenEventEmitter from "./VideoGifGeneratorEvents";
|
||||
|
||||
/**
|
||||
|
@ -25,9 +25,7 @@ const desiredFramerate = 8;
|
|||
*/
|
||||
const gif = new gifJS({
|
||||
workerScript:
|
||||
window.location.origin +
|
||||
"/static/js/" +
|
||||
(canWasm ? "gif.worker-wasm.js" : "gif.worker.js"),
|
||||
basePath + "/static/js/" + (canWasm ? "gif.worker-wasm.js" : "gif.worker.js"),
|
||||
workers: 4,
|
||||
quality: 8,
|
||||
// globalPalette: true,
|
||||
|
@ -118,7 +116,7 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
|
|||
});
|
||||
|
||||
// NOTE: MP4 is hardcoded!
|
||||
videoEl.src = `${window.location.origin}/${fileID}.mp4`;
|
||||
videoEl.src = `${basePath}/${fileID}.mp4`;
|
||||
document.body.appendChild(videoEl);
|
||||
}, []);
|
||||
|
||||
|
@ -172,21 +170,17 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
|
|||
postData.append("data", !isStitchRequest ? blobChunk : null);
|
||||
|
||||
try {
|
||||
await axios.post(
|
||||
window.location.origin + "/api/finishAdminTask.php",
|
||||
postData,
|
||||
{
|
||||
onUploadProgress: p => {
|
||||
const uploadPercent = (p.loaded * 100) / p.total;
|
||||
const chunkCompletePercent = 34 / maxChunkAmount;
|
||||
const progress =
|
||||
(uploadPercent * chunkCompletePercent) / 100 +
|
||||
66 +
|
||||
chunkCompletePercent * chunkNum;
|
||||
setProgress(progress);
|
||||
},
|
||||
}
|
||||
);
|
||||
await axios.post(getApiPath("finishAdminTask"), postData, {
|
||||
onUploadProgress: p => {
|
||||
const uploadPercent = (p.loaded * 100) / p.total;
|
||||
const chunkCompletePercent = 34 / maxChunkAmount;
|
||||
const progress =
|
||||
(uploadPercent * chunkCompletePercent) / 100 +
|
||||
66 +
|
||||
chunkCompletePercent * chunkNum;
|
||||
setProgress(progress);
|
||||
},
|
||||
});
|
||||
|
||||
if (!isStitchRequest) uploadGifChunk(chunkNum + 1);
|
||||
else shiftToNextGif();
|
||||
|
|
|
@ -2,8 +2,10 @@ import React, { useEffect } from "react";
|
|||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
||||
import VideoWorker from "worker-loader!./video.worker.ts";
|
||||
import { toast } from "../../_DesignSystem";
|
||||
import { basePath } from "../../_interceptedAxios";
|
||||
|
||||
let worker: VideoWorker = null;
|
||||
const { baseDirectory } = window;
|
||||
|
||||
/**
|
||||
* - Retrieve a list of missing thumbnails
|
||||
|
@ -72,7 +74,7 @@ const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
|
|||
});
|
||||
|
||||
// NOTE: MP4 is hardcoded, keep that in mind
|
||||
videoEl.src = `${window.location.origin}/${id}.mp4#t=0.1`;
|
||||
videoEl.src = `${basePath}/${id}.mp4#t=0.1`;
|
||||
document.body.appendChild(canvasEl);
|
||||
document.body.appendChild(videoEl);
|
||||
});
|
||||
|
@ -87,7 +89,7 @@ const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
|
|||
case "getFirstFrame":
|
||||
if (typeof data.arguments === "string") {
|
||||
getFirstFrame(data.arguments).then(obj => {
|
||||
worker.postMessage({ task: "setFrame", arguments: obj }, [
|
||||
worker.postMessage({ task: "setFrame", arguments: obj, baseDirectory }, [
|
||||
obj.arrayBuffer,
|
||||
]);
|
||||
});
|
||||
|
@ -109,7 +111,7 @@ const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
worker.postMessage("fetchList");
|
||||
worker.postMessage({ task: "fetchList", baseDirectory });
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
|
|
|
@ -33,9 +33,12 @@ ctx.addEventListener("message", ({ data }) => {
|
|||
case "fetchList":
|
||||
(async () => {
|
||||
try {
|
||||
const res = await axios.get(self.location.origin + "/api/getAdminTasks.php", {
|
||||
params: { type: "video-thumbnail" },
|
||||
});
|
||||
const res = await axios.get(
|
||||
self.location.origin + data.baseDirectory + "/api/getAdminTasks.php",
|
||||
{
|
||||
params: { type: "video-thumbnail" },
|
||||
}
|
||||
);
|
||||
|
||||
if (res.data) {
|
||||
IDList = res.data;
|
||||
|
@ -64,7 +67,10 @@ ctx.addEventListener("message", ({ data }) => {
|
|||
postData.append("id", id);
|
||||
postData.append("data", imageBlob);
|
||||
|
||||
await axios.post(self.location.origin + "/api/finishAdminTask.php", postData);
|
||||
await axios.post(
|
||||
self.location.origin + data.baseDirectory + "/api/finishAdminTask.php",
|
||||
postData
|
||||
);
|
||||
IDList.shift();
|
||||
generateNextThumbnail();
|
||||
} catch (err) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import axiosInstance, { AxiosInstance, AxiosError } from "axios";
|
||||
import { APIMethod } from "./index.types";
|
||||
|
||||
export interface CustomError {
|
||||
code: number;
|
||||
|
@ -6,6 +7,10 @@ export interface CustomError {
|
|||
i18n: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A special instance of Axios which has a built-in
|
||||
* error handler specifically made for API calls to shadis' backend server.
|
||||
*/
|
||||
const axios: AxiosInstance = axiosInstance.create();
|
||||
axios.interceptors.response.use(
|
||||
res => res,
|
||||
|
@ -22,4 +27,16 @@ axios.interceptors.response.use(
|
|||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the root path of the client
|
||||
*/
|
||||
export const basePath: URL["href"] = window.location.origin + window.baseDirectory;
|
||||
|
||||
/**
|
||||
* Generates a URL that can be used to request data from the backend API
|
||||
*/
|
||||
export const getApiPath = (path: APIMethod): URL["href"] => {
|
||||
return basePath + "/api/" + path + ".php";
|
||||
};
|
||||
|
||||
export default axios;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export type APIMethod =
|
||||
| "get"
|
||||
| "getAll"
|
||||
| "edit"
|
||||
| "getAdminTasks"
|
||||
| "finishAdminTask"
|
||||
| "login"
|
||||
| "logout"
|
||||
| "upload";
|
|
@ -23,7 +23,7 @@ i18n
|
|||
ns: [...defaultNs, ...(isLoggedIn ? ["dashboard"] : [])],
|
||||
defaultNS: "common",
|
||||
backend: {
|
||||
loadPath: "/static/locales/{{lng}}/{{ns}}.json",
|
||||
loadPath: "./static/locales/{{lng}}/{{ns}}.json",
|
||||
},
|
||||
detection: {
|
||||
lookupQuerystring: "lang",
|
||||
|
|
Loading…
Reference in New Issue