init
This commit is contained in:
commit
9862f0c0f8
|
@ -0,0 +1,26 @@
|
|||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2021` `AAGaming`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { React } from '@goosemod/webpack/common';
|
||||
import { findByProps } from '@goosemod/webpack';
|
||||
export default class Button extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.classes = findByProps('iconWrapper', 'clickable');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className={`yt-button ${this.classes.clickable}`} onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.props.addPlayer(this.props.id, this.props.timestamp);
|
||||
}}><div className={`${this.classes.icon}`}><svg class="connectedAccountOpenIcon-2cNbq5" aria-hidden="false" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M10 5V3H5.375C4.06519 3 3 4.06519 3 5.375V18.625C3 19.936 4.06519 21 5.375 21H18.625C19.936 21 21 19.936 21 18.625V14H19V19H5V5H10Z"></path><path fill="currentColor" d="M21 2.99902H14V4.99902H17.586L9.29297 13.292L10.707 14.706L19 6.41302V9.99902H21V2.99902Z"></path></svg></div></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
import { React } from '@goosemod/webpack/common';
|
||||
import Player from './Player.jsx';
|
||||
const {useState, useEffect} = React;
|
||||
|
||||
export default function YoutubePopoutController ({ setAddPlayer }) {
|
||||
const [player, setPlayer] = useState(null);
|
||||
useEffect(() => setAddPlayer((id, timestamp) => {
|
||||
setPlayer(<Player id={id} timestamp={timestamp} unmount={() => {
|
||||
setPlayer(null);
|
||||
}} />)
|
||||
}), []);
|
||||
return player;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { React } from '@goosemod/webpack/common';
|
||||
import { findByDisplayName, find } from '@goosemod/webpack';
|
||||
const Icon = findByDisplayName("Close");
|
||||
const Draggable = find(x => x?.default?.displayName === 'Draggable' && x?.default?.prototype.animateToPosition)?.default;
|
||||
module.exports = React.memo(class Player extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
closing: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateWindowDimensions();
|
||||
window.addEventListener('resize', this.updateWindowDimensions);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.updateWindowDimensions);
|
||||
this.props.unmount?.();
|
||||
}
|
||||
|
||||
updateWindowDimensions = () => {
|
||||
this.setState({
|
||||
...this.state, width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState({ ...this.state, closing: true });
|
||||
setTimeout(() => {
|
||||
this.componentWillUnmount()
|
||||
}, 300);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Draggable
|
||||
dragAnywhere={true}
|
||||
className='pictureInPictureWindow-1B5qSe playerDrag'
|
||||
maxX={this.state.width}
|
||||
maxY={this.state.height}
|
||||
initialX={50}
|
||||
initialY={50}
|
||||
initialPosition={{top:0, left: 0}}
|
||||
>
|
||||
<div className={`playerContent ${this.state.closing ? 'playerClose' : ''}`}>
|
||||
<div className='playerTitle'>
|
||||
<div onClick={() => this.close()}>
|
||||
<Icon className="playerCloseButton" style={{ float: 'right', color: '#ffffff' }} />
|
||||
</div>
|
||||
</div>
|
||||
<iframe className="playerVideo" width="400" height="225" src={`https://www.youtube.com/embed/${this.props.id}?autoplay=1` + (this.props.timestamp ? `&start=${this.props.timestamp}` : '')} frameBorder="0" allowFullScreen="" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-presentation" />
|
||||
</div>
|
||||
</Draggable>
|
||||
</>)
|
||||
}
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "Youtube Popouts",
|
||||
"description": "Adds a button to make youtube video embeds load in a floating picture in picture",
|
||||
"version": "1.0.0",
|
||||
"authors": [
|
||||
"373833473091436546"
|
||||
],
|
||||
"tags": [
|
||||
"ui"
|
||||
],
|
||||
"main": "index.js"
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import Plugin from '@goosemod/plugin';
|
||||
import { find } from '@goosemod/webpack';
|
||||
import { React, ReactDOM } from '@goosemod/webpack/common';
|
||||
import { inject, uninject } from '@goosemod/patcher';
|
||||
|
||||
import Button from './components/Button.jsx';
|
||||
import style from './style.css.js';
|
||||
import YoutubePopoutController from './components/Controller.jsx';
|
||||
|
||||
class YoutubePopouts extends Plugin {
|
||||
isYouTubeLink(url) {
|
||||
return (/^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(\?\S*)?$/gi).test(
|
||||
url
|
||||
);
|
||||
}
|
||||
|
||||
getYouTubeVideoID(url) {
|
||||
const regExp = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#\&\?]*).*/;
|
||||
const match = url.match(regExp);
|
||||
return match && match[1].length == 11 ? match[1] : false;
|
||||
}
|
||||
|
||||
getYouTubeTimestamp(url) {
|
||||
const regExp = /.*(?:t=)([^#\&\?]*).*/;
|
||||
const match = url.match(regExp);
|
||||
return match && match[1];
|
||||
}
|
||||
|
||||
onImport() {
|
||||
if (document.getElementById("youtubepopouts-css")) document.getElementById("youtubepopouts-css").remove();
|
||||
this.css = Object.assign(document.createElement("style"), {
|
||||
textContent: style,
|
||||
id: "youtubepopouts-css"
|
||||
});
|
||||
document.head.appendChild(this.css);
|
||||
|
||||
this.element = document.createElement('div');
|
||||
this.element.classList.add('playerContainer');
|
||||
document.getElementById('app-mount').appendChild(this.element);
|
||||
let addPlayer = () => {};
|
||||
ReactDOM.render(React.createElement(YoutubePopoutController, {setAddPlayer: (add) => addPlayer = add}), this.element);
|
||||
const Anchor = find(m => m?.default?.displayName === 'Anchor');
|
||||
inject('youtube-link', Anchor, 'default', (args, res) => {
|
||||
if (!args[0].href || args[0]?.className?.includes('iconWrapper-21idzA')) {
|
||||
return res;
|
||||
}
|
||||
if (this.isYouTubeLink(args[0].href)) {
|
||||
// console.log(res.props.children);
|
||||
res.props.children = [res.props.children];
|
||||
res.props.children.push(React.createElement(Button, { id: this.getYouTubeVideoID(args[0].href), timestamp: this.getYouTubeTimestamp(args[0].href), addPlayer }));
|
||||
}
|
||||
// console.log(args, res);
|
||||
return res;
|
||||
}, false);
|
||||
Anchor.default.displayName = 'Anchor';
|
||||
}
|
||||
|
||||
onRemove() {
|
||||
this.css.remove();
|
||||
uninject("youtube-link")
|
||||
document.getElementById('app-mount').removeChild(document.querySelector('.playerContainer'));
|
||||
}
|
||||
};
|
||||
|
||||
export default new YoutubePopouts();
|
|
@ -0,0 +1,63 @@
|
|||
const style = `.playerContainer {
|
||||
z-index: 49873289154659285;
|
||||
width: auto;
|
||||
height: auto;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.yt-button {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.playerTitle {
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
cursor: move;
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.playerVideo {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.playerClose {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.playerDrag {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* animations */
|
||||
|
||||
.playerContent {
|
||||
animation: open 250ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
.playerClose {
|
||||
animation: close 250ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes close {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
to {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes open {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
99% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
}
|
||||
}`
|
||||
export default style;
|
Loading…
Reference in New Issue