Skip to content

Commit

Permalink
Shift+Click URL to add list of URLs to Library/Scene
Browse files Browse the repository at this point in the history
  • Loading branch information
StarrHelixx committed Dec 13, 2023
1 parent 9a260c6 commit 511cd4d
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 51 deletions.
12 changes: 10 additions & 2 deletions src/renderer/components/library/Library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ import BatchClipDialog from "./BatchClipDialog";
import LibrarySearch from "./LibrarySearch";
import SourceIcon from "./SourceIcon";
import SourceList from "./SourceList";
import URLDialog from "../sceneDetail/URLDialog";
import GooninatorDialog from "../sceneDetail/GooninatorDialog";
import PiwigoDialog from "../sceneDetail/PiwigoDialog";
import URLDialog from "../sceneDetail/URLDialog";

const drawerWidth = 240;

Expand Down Expand Up @@ -1017,6 +1018,11 @@ class Library extends React.Component {
</MenuItem>
</Menu>

<GooninatorDialog
open={this.state.openMenu == MO.gooninatorImport}
onImportURL={this.onAddSource.bind(this)}
onClose={this.onCloseDialog.bind(this)}
/>
<URLDialog
open={this.state.openMenu == MO.urlImport}
onImportURL={this.onAddSource.bind(this)}
Expand Down Expand Up @@ -1114,7 +1120,7 @@ class Library extends React.Component {
// Use alt+L to move cached offline sources to local sources
onKeyDown = (e: KeyboardEvent) => {
if (!e.shiftKey && !e.ctrlKey && e.altKey && (e.key == 'p' || e.key == 'π')) {
this.setState({openMenu: this.state.openMenu == MO.urlImport ? null : MO.urlImport});
this.setState({openMenu: this.state.openMenu == MO.gooninatorImport ? null : MO.gooninatorImport});
} else if (!e.shiftKey && !e.ctrlKey && e.altKey && (e.key == 'm' || e.key == 'µ')) {
this.toggleMarked();
} else if (!e.shiftKey && !e.ctrlKey && e.altKey && (e.key == 'l' || e.key == '¬')) {
Expand Down Expand Up @@ -1247,6 +1253,8 @@ class Library extends React.Component {
this.onCloseDialog();
if (addFunction == AF.videos && e.shiftKey) {
this.props.onAddSource(null, AF.videoDir, ...args);
} else if (addFunction == AF.url && e.shiftKey) {
this.setState({openMenu: MO.urlImport});
} else {
this.props.onAddSource(null, addFunction, ...args);
}
Expand Down
130 changes: 130 additions & 0 deletions src/renderer/components/sceneDetail/GooninatorDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as React from "react";
import {remote} from "electron";

import {
Button,
Collapse,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
Theme,
} from "@mui/material";

import createStyles from '@mui/styles/createStyles';
import withStyles from '@mui/styles/withStyles';

import {GT} from "../../data/const";

const styles = (theme: Theme) => createStyles({
root: {
display: 'flex',
},
rootInput: {
marginLeft: theme.spacing(2),
flexGrow: 1,
},
});

class GooninatorDialog extends React.Component {
readonly props: {
classes: any,
open: boolean,
onClose(): void,
onImportURL(type: string, e: MouseEvent, ...args: any[]): void,
};

readonly state = {
importType: GT.tumblr,
importURL: "",
rootDir: "",
};

render() {
const classes = this.props.classes;
return (
<Dialog
open={this.props.open}
onClose={this.props.onClose.bind(this)}
aria-labelledby="url-import-title"
aria-describedby="url-import-description">
<DialogTitle id="url-import-title">Import URL</DialogTitle>
<DialogContent>
<DialogContentText id="remove-all-description">
Paste a gooninator URL and choose how to import the sources:
</DialogContentText>
<TextField
variant="standard"
label="Gooninator URL"
fullWidth
placeholder="Paste URL Here"
margin="dense"
value={this.state.importURL}
onChange={this.onURLChange.bind(this)} />
<div className={classes.root}>
<FormControl variant="standard">
<InputLabel>Import as</InputLabel>
<Select
variant="standard"
value={this.state.importType}
onChange={this.onTypeChange.bind(this)}>
<MenuItem value={GT.tumblr}>Tumblr Blogs</MenuItem>
<MenuItem value={GT.local}>Local Directories</MenuItem>
</Select>
</FormControl>
<Collapse className={classes.rootInput} in={this.state.importType == GT.local}>
<TextField
variant="standard"
fullWidth
label="Parent Directory"
value={this.state.rootDir}
InputProps={{readOnly: true}}
onClick={this.onRootChange.bind(this)} />
</Collapse>
</div>
</DialogContent>
<DialogActions>
<Button onClick={this.props.onClose.bind(this)} color="secondary">
Cancel
</Button>
<Button
disabled={!this.state.importURL.match("^https?://") || (this.state.importType == GT.local && this.state.rootDir.length == 0)}
onClick={this.onImportURL.bind(this)}
color="primary">
Import
</Button>
</DialogActions>
</Dialog>
);
}

onTypeChange(e: MouseEvent) {
const type = (e.target as HTMLInputElement).value;
this.setState({importType: type});
}

onURLChange(e: MouseEvent) {
const type = (e.target as HTMLInputElement).value;
this.setState({importURL: type});
}

onRootChange() {
let result = remote.dialog.showOpenDialog(remote.getCurrentWindow(), {properties: ['openDirectory']});
if (!result || !result.length) return;
this.setState({rootDir: result[0]});
}

onImportURL() {
this.props.onImportURL(this.state.importType, null, this.state.importURL, this.state.rootDir);
this.props.onClose();
}
}

(GooninatorDialog as any).displayName="GooninatorDialog";
export default withStyles(styles)(GooninatorDialog as any);
15 changes: 11 additions & 4 deletions src/renderer/components/sceneDetail/SceneDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ import WeightGroup from "../../data/WeightGroup";
import SceneEffects from "./SceneEffects";
import SceneGenerator from "./SceneGenerator";
import SceneOptions from "./SceneOptions";
import URLDialog from "./URLDialog";
import GooninatorDialog from "./GooninatorDialog";
import LibrarySearch from "../library/LibrarySearch";
import SourceList from "../library/SourceList";
import AudioTextEffects from "./AudioTextEffects";
Expand All @@ -90,6 +90,7 @@ import CaptionScript from "../../data/CaptionScript";
import SceneGrid from "../../data/SceneGrid";
import PiwigoDialog from "./PiwigoDialog";
import SourceIcon from "../library/SourceIcon";
import URLDialog from "./URLDialog";

const drawerWidth = 240;

Expand Down Expand Up @@ -899,12 +900,16 @@ class SceneDetail extends React.Component {
<AddIcon className={classes.icon} />
</Fab>

<GooninatorDialog
open={this.state.openMenu == MO.gooninatorImport}
onImportURL={this.onAddSource.bind(this)}
onClose={this.onCloseDialog.bind(this)}
/>
<URLDialog
open={this.state.openMenu == MO.urlImport}
onImportURL={this.onAddSource.bind(this)}
onClose={this.onCloseDialog.bind(this)}
/>

<PiwigoDialog
config={this.props.config}
open={this.state.openMenu == MO.piwigo}
Expand Down Expand Up @@ -1184,7 +1189,7 @@ class SceneDetail extends React.Component {
// Use alt+U to toggle highlighting untagged sources
onKeyDown = (e: KeyboardEvent) => {
if (!e.shiftKey && !e.ctrlKey && e.altKey && (e.key == 'p' || e.key == 'π')) {
this.setState({openMenu: this.state.openMenu == MO.urlImport ? null : MO.urlImport});
this.setState({openMenu: this.state.openMenu == MO.gooninatorImport ? null : MO.gooninatorImport});
}
};

Expand Down Expand Up @@ -1280,7 +1285,9 @@ class SceneDetail extends React.Component {
this.props.onTutorial(SDT.add2);
this.props.onAddSource(this.props.scene, "tutorial");
} else if (addFunction == AF.videos && e.shiftKey) {
this.props.onAddSource(this.props.scene, AF.videoDir, ...args);
this.props.onAddSource(this.props.scene, AF.videoDir, ...args);
} else if (addFunction == AF.url && e.shiftKey) {
this.setState({openMenu: MO.urlImport});
} else {
this.props.onAddSource(this.props.scene, addFunction, ...args);
}
Expand Down
63 changes: 18 additions & 45 deletions src/renderer/components/sceneDetail/URLDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import createStyles from '@mui/styles/createStyles';
import withStyles from '@mui/styles/withStyles';

import {GT} from "../../data/const";
import {AF} from "../../data/const";

const styles = (theme: Theme) => createStyles({
root: {
Expand All @@ -30,6 +30,13 @@ const styles = (theme: Theme) => createStyles({
marginLeft: theme.spacing(2),
flexGrow: 1,
},
urlInput: {
minWidth: 550,
minHeight: 300,
whiteSpace: 'nowrap',
overflowX: 'hidden',
overflowY: 'auto !important' as any,
},
});

class URLDialog extends React.Component {
Expand All @@ -41,9 +48,7 @@ class URLDialog extends React.Component {
};

readonly state = {
importType: GT.tumblr,
importURL: "",
rootDir: "",
importURLs: "",
};

render() {
Expand All @@ -54,74 +59,42 @@ class URLDialog extends React.Component {
onClose={this.props.onClose.bind(this)}
aria-labelledby="url-import-title"
aria-describedby="url-import-description">
<DialogTitle id="url-import-title">Import URL</DialogTitle>
<DialogTitle id="url-import-title">Add Multiple URL Sources</DialogTitle>
<DialogContent>
<DialogContentText id="remove-all-description">
Paste a gooninator URL and choose how to import the sources:
Paste URLs to add as sources, one per line:
</DialogContentText>
<TextField
variant="standard"
label="Gooninator URL"
label="Source URLs"
fullWidth
placeholder="Paste URL Here"
multiline
margin="dense"
value={this.state.importURL}
value={this.state.importURLs}
inputProps={{className: classes.urlInput}}
onChange={this.onURLChange.bind(this)} />
<div className={classes.root}>
<FormControl variant="standard">
<InputLabel>Import as</InputLabel>
<Select
variant="standard"
value={this.state.importType}
onChange={this.onTypeChange.bind(this)}>
<MenuItem value={GT.tumblr}>Tumblr Blogs</MenuItem>
<MenuItem value={GT.local}>Local Directories</MenuItem>
</Select>
</FormControl>
<Collapse className={classes.rootInput} in={this.state.importType == GT.local}>
<TextField
variant="standard"
fullWidth
label="Parent Directory"
value={this.state.rootDir}
InputProps={{readOnly: true}}
onClick={this.onRootChange.bind(this)} />
</Collapse>
</div>
</DialogContent>
<DialogActions>
<Button onClick={this.props.onClose.bind(this)} color="secondary">
Cancel
</Button>
<Button
disabled={!this.state.importURL.match("^https?://") || (this.state.importType == GT.local && this.state.rootDir.length == 0)}
onClick={this.onImportURL.bind(this)}
color="primary">
Import
Add Sources
</Button>
</DialogActions>
</Dialog>
);
}

onTypeChange(e: MouseEvent) {
const type = (e.target as HTMLInputElement).value;
this.setState({importType: type});
}

onURLChange(e: MouseEvent) {
const type = (e.target as HTMLInputElement).value;
this.setState({importURL: type});
}

onRootChange() {
let result = remote.dialog.showOpenDialog(remote.getCurrentWindow(), {properties: ['openDirectory']});
if (!result || !result.length) return;
this.setState({rootDir: result[0]});
this.setState({importURLs: type});
}

onImportURL() {
this.props.onImportURL(this.state.importType, null, this.state.importURL, this.state.rootDir);
this.props.onImportURL(AF.list, null, this.state.importURLs);
this.props.onClose();
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/renderer/data/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,17 @@ export function addSource(state: State, scene: Scene, type: string, ...args: any
}
}

case AF.list:
let newSources = Array.from(args[0].trim().split("\n")).filter((s: string) => s.length > 0) as Array<string>;
if (scene != null) {
return updateScene(state, scene, (s) => {
addSources(s.sources, newSources, state.library);
handleArgs(s);
})
} else {
return updateLibrary(state, (l) => addSources(l, newSources, state.library));
}

case AF.directory:
let dResult = remote.dialog.showOpenDialog(remote.getCurrentWindow(), {properties: ['openDirectory', 'multiSelections']});
if (!dResult) return;
Expand Down Expand Up @@ -2425,6 +2436,7 @@ function mergeSources(originalSources: Array<LibrarySource>, newSources: Array<L

function addSources(originalSources: Array<LibrarySource>, newSources: Array<string>, library: Array<LibrarySource>) {
// dedup
newSources = [...new Set(newSources)];
let sourceURLs = originalSources.map((s) => s.url);
newSources = newSources.filter((s) => !sourceURLs.includes(s));

Expand Down
2 changes: 2 additions & 0 deletions src/renderer/data/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ const SL = { // strobe layers

const AF = { // add functions
url: 'af.url',
list: 'af.list',
directory: 'af.directory',
videos: 'af.videos',
videoDir: 'af.videodir',
Expand All @@ -202,6 +203,7 @@ const MO = { // Menu/Modal Constants
playlist: 'mo.playlist',
newPlaylist: 'mo.newplaylist',
playlistDuplicates: 'mo.playlistduplicates',
gooninatorImport: 'mo.gooninatorimport',
urlImport: 'mo.urlimport',
libraryImport: 'mo.libraryImport',
newWindowAlert: 'mo.nwalert',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/data/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export default new Map<string, string>([
['sl.bottom', 'Behind All'],

['af.url', '+ URL'],
['af.list', '+ List'],
['af.audios', '+ Local audio'],
['af.directory', '+ Local directory'],
['af.videos', '+ Local video/playlist'],
Expand Down

0 comments on commit 511cd4d

Please sign in to comment.