Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
28f2fbd6a7 | ||
|
d19190bf6a | ||
|
b14309daeb | ||
|
990cb9aaec | ||
|
bb4e5ecb50 | ||
|
ff94c324b3 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,3 +12,5 @@ backend/.env
|
|||||||
|
|
||||||
# JavaScript stuff
|
# JavaScript stuff
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
.editorconfig
|
||||||
|
14
Dockerfile
14
Dockerfile
@ -1,14 +1,18 @@
|
|||||||
# multi stage to build tube archivist
|
# multi stage to build tube archivist
|
||||||
# build python wheel, download and extract ffmpeg, copy into final image
|
# build python wheel, download and extract ffmpeg, copy into final image
|
||||||
|
|
||||||
|
FROM node:lts-alpine AS npm-builder
|
||||||
|
COPY frontend/package.json frontend/package-lock.json /
|
||||||
|
RUN npm i
|
||||||
|
|
||||||
FROM node:lts-alpine AS node-builder
|
FROM node:lts-alpine AS node-builder
|
||||||
|
|
||||||
# RUN npm config set registry https://registry.npmjs.org/
|
# RUN npm config set registry https://registry.npmjs.org/
|
||||||
|
|
||||||
|
COPY --from=npm-builder ./node_modules /frontend/node_modules
|
||||||
COPY ./frontend /frontend
|
COPY ./frontend /frontend
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
RUN npm i
|
|
||||||
RUN npm run build:deploy
|
RUN npm run build:deploy
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
@ -54,9 +58,9 @@ RUN apt-get clean && apt-get -y update && apt-get -y install --no-install-recomm
|
|||||||
|
|
||||||
# install debug tools for testing environment
|
# install debug tools for testing environment
|
||||||
RUN if [ "$INSTALL_DEBUG" ] ; then \
|
RUN if [ "$INSTALL_DEBUG" ] ; then \
|
||||||
apt-get -y update && apt-get -y install --no-install-recommends \
|
apt-get -y update && apt-get -y install --no-install-recommends \
|
||||||
vim htop bmon net-tools iputils-ping procps lsof \
|
vim htop bmon net-tools iputils-ping procps lsof \
|
||||||
&& pip install --user ipython pytest pytest-django \
|
&& pip install --user ipython pytest pytest-django \
|
||||||
; fi
|
; fi
|
||||||
|
|
||||||
# make folders
|
# make folders
|
||||||
|
@ -47,7 +47,11 @@ const DownloadListItem = ({ download, setRefresh }: DownloadListItemProps) => {
|
|||||||
|
|
||||||
{!download.channel_indexed && <span>{download.channel_name}</span>}
|
{!download.channel_indexed && <span>{download.channel_name}</span>}
|
||||||
|
|
||||||
<a href={`https://www.youtube.com/watch?v=${download.youtube_id}`} target="_blank">
|
<a
|
||||||
|
href={`https://www.youtube.com/watch?v=${download.youtube_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
<h3>{download.title}</h3>
|
<h3>{download.title}</h3>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import LoadingIndicator from './LoadingIndicator';
|
||||||
|
|
||||||
type InputTextProps = {
|
type InputTextProps = {
|
||||||
type: 'text' | 'number';
|
type: 'text' | 'number';
|
||||||
@ -51,13 +52,7 @@ const InputConfig = ({ type, name, value, setValue, oldValue, updateCallback }:
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{oldValue !== null && <button onClick={() => handleUpdate(name, null)}>reset</button>}
|
{oldValue !== null && <button onClick={() => handleUpdate(name, null)}>reset</button>}
|
||||||
{loading && (
|
{loading && <LoadingIndicator />}
|
||||||
<>
|
|
||||||
<div className="lds-ring" style={{ color: 'var(--accent-font-dark)' }}>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{success && <span>✅</span>}
|
{success && <span>✅</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
9
frontend/src/components/LoadingIndicator.tsx
Normal file
9
frontend/src/components/LoadingIndicator.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const LoadingIndicator = () => {
|
||||||
|
return (
|
||||||
|
<div className="lds-ring" style={{ color: 'var(--accent-font-dark)' }}>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoadingIndicator;
|
@ -140,6 +140,8 @@ const VideoPlayer = ({
|
|||||||
const [showHelpDialog, setShowHelpDialog] = useState(false);
|
const [showHelpDialog, setShowHelpDialog] = useState(false);
|
||||||
const [showInfoDialog, setShowInfoDialog] = useState(false);
|
const [showInfoDialog, setShowInfoDialog] = useState(false);
|
||||||
const [infoDialogContent, setInfoDialogContent] = useState('');
|
const [infoDialogContent, setInfoDialogContent] = useState('');
|
||||||
|
const [isTheaterMode, setIsTheaterMode] = useState(false);
|
||||||
|
const [theaterModeKeyPressed, setTheaterModeKeyPressed] = useState(false);
|
||||||
|
|
||||||
const questionmarkPressed = useKeyPress('?');
|
const questionmarkPressed = useKeyPress('?');
|
||||||
const mutePressed = useKeyPress('m');
|
const mutePressed = useKeyPress('m');
|
||||||
@ -151,6 +153,8 @@ const VideoPlayer = ({
|
|||||||
const arrowRightPressed = useKeyPress('ArrowRight');
|
const arrowRightPressed = useKeyPress('ArrowRight');
|
||||||
const arrowLeftPressed = useKeyPress('ArrowLeft');
|
const arrowLeftPressed = useKeyPress('ArrowLeft');
|
||||||
const pPausedPressed = useKeyPress('p');
|
const pPausedPressed = useKeyPress('p');
|
||||||
|
const theaterModePressed = useKeyPress('t');
|
||||||
|
const escapePressed = useKeyPress('Escape');
|
||||||
|
|
||||||
const videoId = video.youtube_id;
|
const videoId = video.youtube_id;
|
||||||
const videoUrl = video.media_url;
|
const videoUrl = video.media_url;
|
||||||
@ -345,10 +349,42 @@ const VideoPlayer = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [questionmarkPressed]);
|
}, [questionmarkPressed]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (embed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theaterModePressed && !theaterModeKeyPressed) {
|
||||||
|
setTheaterModeKeyPressed(true);
|
||||||
|
|
||||||
|
const newTheaterMode = !isTheaterMode;
|
||||||
|
setIsTheaterMode(newTheaterMode);
|
||||||
|
|
||||||
|
infoDialog(newTheaterMode ? 'Theater mode' : 'Normal mode');
|
||||||
|
} else if (!theaterModePressed) {
|
||||||
|
setTheaterModeKeyPressed(false);
|
||||||
|
}
|
||||||
|
}, [theaterModePressed, isTheaterMode, theaterModeKeyPressed]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (embed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escapePressed && isTheaterMode) {
|
||||||
|
setIsTheaterMode(false);
|
||||||
|
|
||||||
|
infoDialog('Normal mode');
|
||||||
|
}
|
||||||
|
}, [escapePressed, isTheaterMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div id="player" className={embed ? '' : 'player-wrapper'}>
|
<div
|
||||||
<div className={embed ? '' : 'video-main'}>
|
id="player"
|
||||||
|
className={embed ? '' : `player-wrapper ${isTheaterMode ? 'theater-mode' : ''}`}
|
||||||
|
>
|
||||||
|
<div className={embed ? '' : `video-main ${isTheaterMode ? 'theater-mode' : ''}`}>
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
key={`${getApiUrl()}${videoUrl}`}
|
key={`${getApiUrl()}${videoUrl}`}
|
||||||
@ -423,6 +459,18 @@ const VideoPlayer = ({
|
|||||||
<td>Toggle fullscreen</td>
|
<td>Toggle fullscreen</td>
|
||||||
<td>f</td>
|
<td>f</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{!embed && (
|
||||||
|
<>
|
||||||
|
<tr>
|
||||||
|
<td>Toggle theater mode</td>
|
||||||
|
<td>t</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Exit theater mode</td>
|
||||||
|
<td>Esc</td>
|
||||||
|
</tr>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<tr>
|
<tr>
|
||||||
<td>Toggle subtitles (if available)</td>
|
<td>Toggle subtitles (if available)</td>
|
||||||
<td>c</td>
|
<td>c</td>
|
||||||
@ -467,11 +515,19 @@ const VideoPlayer = ({
|
|||||||
<h4>
|
<h4>
|
||||||
This video doesn't have any sponsor segments added. To add a segment go to{' '}
|
This video doesn't have any sponsor segments added. To add a segment go to{' '}
|
||||||
<u>
|
<u>
|
||||||
<a href={`https://www.youtube.com/watch?v=${videoId}`}>this video on YouTube</a>
|
<a
|
||||||
|
href={`https://www.youtube.com/watch?v=${videoId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
this video on YouTube
|
||||||
|
</a>
|
||||||
</u>{' '}
|
</u>{' '}
|
||||||
and add a segment using the{' '}
|
and add a segment using the{' '}
|
||||||
<u>
|
<u>
|
||||||
<a href="https://sponsor.ajay.app/">SponsorBlock</a>
|
<a href="https://sponsor.ajay.app/" target="_blank" rel="noopener noreferrer">
|
||||||
|
SponsorBlock
|
||||||
|
</a>
|
||||||
</u>{' '}
|
</u>{' '}
|
||||||
extension.
|
extension.
|
||||||
</h4>
|
</h4>
|
||||||
@ -480,11 +536,19 @@ const VideoPlayer = ({
|
|||||||
<h4>
|
<h4>
|
||||||
This video has unlocked sponsor segments. Go to{' '}
|
This video has unlocked sponsor segments. Go to{' '}
|
||||||
<u>
|
<u>
|
||||||
<a href={`https://www.youtube.com/watch?v=${videoId}`}>this video on YouTube</a>
|
<a
|
||||||
|
href={`https://www.youtube.com/watch?v=${videoId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
this video on YouTube
|
||||||
|
</a>
|
||||||
</u>{' '}
|
</u>{' '}
|
||||||
and vote on the segments using the{' '}
|
and vote on the segments using the{' '}
|
||||||
<u>
|
<u>
|
||||||
<a href="https://sponsor.ajay.app/">SponsorBlock</a>
|
<a href="https://sponsor.ajay.app/" target="_blank" rel="noopener noreferrer">
|
||||||
|
SponsorBlock
|
||||||
|
</a>
|
||||||
</u>{' '}
|
</u>{' '}
|
||||||
extension.
|
extension.
|
||||||
</h4>
|
</h4>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import iconUnseen from '/img/icon-unseen.svg';
|
import iconUnseen from '/img/icon-unseen.svg';
|
||||||
import iconSeen from '/img/icon-seen.svg';
|
import iconSeen from '/img/icon-seen.svg';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import LoadingIndicator from './LoadingIndicator';
|
||||||
|
|
||||||
type WatchedCheckBoxProps = {
|
type WatchedCheckBoxProps = {
|
||||||
watched: boolean;
|
watched: boolean;
|
||||||
@ -32,9 +33,7 @@ const WatchedCheckBox = ({ watched, onClick, onDone }: WatchedCheckBoxProps) =>
|
|||||||
<>
|
<>
|
||||||
{loading && (
|
{loading && (
|
||||||
<>
|
<>
|
||||||
<div className="lds-ring" style={{ color: 'var(--accent-font-dark)' }}>
|
<LoadingIndicator />
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!loading && watched && (
|
{!loading && watched && (
|
||||||
|
@ -130,7 +130,11 @@ const ChannelAbout = () => {
|
|||||||
{channel.channel_active && (
|
{channel.channel_active && (
|
||||||
<p>
|
<p>
|
||||||
Youtube:{' '}
|
Youtube:{' '}
|
||||||
<a href={`https://www.youtube.com/channel/${channel.channel_id}`} target="_blank">
|
<a
|
||||||
|
href={`https://www.youtube.com/channel/${channel.channel_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Active
|
Active
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@ -316,7 +320,7 @@ const ChannelAbout = () => {
|
|||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Overwrite{' '}
|
Overwrite{' '}
|
||||||
<a href="https://sponsor.ajay.app/" target="_blank">
|
<a href="https://sponsor.ajay.app/" target="_blank" rel="noopener noreferrer">
|
||||||
SponsorBlock
|
SponsorBlock
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -97,104 +97,97 @@ const ChannelVideo = ({ videoType }: ChannelVideoProps) => {
|
|||||||
videoId,
|
videoId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!channel) {
|
|
||||||
return (
|
|
||||||
<div className="boxed-content">
|
|
||||||
<br />
|
|
||||||
<h2>Channel {channelId} not found!</h2>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
channel && (
|
||||||
<title>{`TA | Channel: ${channel.channel_name}`}</title>
|
<>
|
||||||
<ScrollToTopOnNavigate />
|
<title>{`TA | Channel: ${channel.channel_name}`}</title>
|
||||||
<div className="boxed-content">
|
<ScrollToTopOnNavigate />
|
||||||
<div className="info-box info-box-2">
|
<div className="boxed-content">
|
||||||
<ChannelOverview
|
<div className="info-box info-box-2">
|
||||||
channelId={channel.channel_id}
|
<ChannelOverview
|
||||||
channelname={channel.channel_name}
|
channelId={channel.channel_id}
|
||||||
channelSubs={channel.channel_subs}
|
channelname={channel.channel_name}
|
||||||
channelSubscribed={channel.channel_subscribed}
|
channelSubs={channel.channel_subs}
|
||||||
channelThumbUrl={channel.channel_thumb_url}
|
channelSubscribed={channel.channel_subscribed}
|
||||||
setRefresh={setRefresh}
|
channelThumbUrl={channel.channel_thumb_url}
|
||||||
/>
|
setRefresh={setRefresh}
|
||||||
<div className="info-box-item">
|
/>
|
||||||
{videoAggs && (
|
<div className="info-box-item">
|
||||||
<>
|
{videoAggs && (
|
||||||
<p>
|
<>
|
||||||
{videoAggs.total_items.value} videos <span className="space-carrot">|</span>{' '}
|
<p>
|
||||||
{videoAggs.total_duration.value_str} playback{' '}
|
{videoAggs.total_items.value} videos <span className="space-carrot">|</span>{' '}
|
||||||
<span className="space-carrot">|</span> Total size{' '}
|
{videoAggs.total_duration.value_str} playback{' '}
|
||||||
{humanFileSize(videoAggs.total_size.value, useSiUnits)}
|
<span className="space-carrot">|</span> Total size{' '}
|
||||||
</p>
|
{humanFileSize(videoAggs.total_size.value, useSiUnits)}
|
||||||
<div className="button-box">
|
</p>
|
||||||
<Button
|
<div className="button-box">
|
||||||
label="Mark as watched"
|
<Button
|
||||||
id="watched-button"
|
label="Mark as watched"
|
||||||
type="button"
|
id="watched-button"
|
||||||
title={`Mark all videos from ${channel.channel_name} as watched`}
|
type="button"
|
||||||
onClick={async () => {
|
title={`Mark all videos from ${channel.channel_name} as watched`}
|
||||||
await updateWatchedState({
|
onClick={async () => {
|
||||||
id: channel.channel_id,
|
await updateWatchedState({
|
||||||
is_watched: true,
|
id: channel.channel_id,
|
||||||
});
|
is_watched: true,
|
||||||
|
});
|
||||||
|
|
||||||
setRefresh(true);
|
setRefresh(true);
|
||||||
}}
|
}}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
<Button
|
<Button
|
||||||
label="Mark as unwatched"
|
label="Mark as unwatched"
|
||||||
id="unwatched-button"
|
id="unwatched-button"
|
||||||
type="button"
|
type="button"
|
||||||
title={`Mark all videos from ${channel.channel_name} as unwatched`}
|
title={`Mark all videos from ${channel.channel_name} as unwatched`}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await updateWatchedState({
|
await updateWatchedState({
|
||||||
id: channel.channel_id,
|
id: channel.channel_id,
|
||||||
is_watched: false,
|
is_watched: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
setRefresh(true);
|
setRefresh(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`boxed-content ${gridView}`}>
|
<div className={`boxed-content ${gridView}`}>
|
||||||
<Filterbar
|
<Filterbar
|
||||||
hideToggleText={'Hide watched videos:'}
|
hideToggleText={'Hide watched videos:'}
|
||||||
viewStyle={ViewStyleNames.Home as ViewStyleNamesType}
|
viewStyle={ViewStyleNames.Home as ViewStyleNamesType}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<EmbeddableVideoPlayer videoId={videoId} />
|
|
||||||
|
|
||||||
<div className={`boxed-content ${gridView}`}>
|
|
||||||
<div className={`video-list ${viewStyle} ${gridViewGrid}`}>
|
|
||||||
{!hasVideos && (
|
|
||||||
<>
|
|
||||||
<h2>No videos found...</h2>
|
|
||||||
<p>
|
|
||||||
Try going to the <Link to={Routes.Downloads}>downloads page</Link> to start the scan
|
|
||||||
and download tasks.
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<VideoList videoList={videoList} viewStyle={viewStyle} refreshVideoList={setRefresh} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{pagination && (
|
<EmbeddableVideoPlayer videoId={videoId} />
|
||||||
<div className="boxed-content">
|
|
||||||
<Pagination pagination={pagination} setPage={setCurrentPage} />
|
<div className={`boxed-content ${gridView}`}>
|
||||||
|
<div className={`video-list ${viewStyle} ${gridViewGrid}`}>
|
||||||
|
{!hasVideos && (
|
||||||
|
<>
|
||||||
|
<h2>No videos found...</h2>
|
||||||
|
<p>
|
||||||
|
Try going to the <Link to={Routes.Downloads}>downloads page</Link> to start the
|
||||||
|
scan and download tasks.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<VideoList videoList={videoList} viewStyle={viewStyle} refreshVideoList={setRefresh} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{pagination && (
|
||||||
</>
|
<div className="boxed-content">
|
||||||
|
<Pagination pagination={pagination} setPage={setCurrentPage} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import Colours from '../configuration/colours/Colours';
|
|||||||
import Button from '../components/Button';
|
import Button from '../components/Button';
|
||||||
import signIn from '../api/actions/signIn';
|
import signIn from '../api/actions/signIn';
|
||||||
import loadAuth from '../api/loader/loadAuth';
|
import loadAuth from '../api/loader/loadAuth';
|
||||||
|
import LoadingIndicator from '../components/LoadingIndicator';
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -134,10 +135,7 @@ const Login = () => {
|
|||||||
{waitingForBackend && (
|
{waitingForBackend && (
|
||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
Waiting for backend{' '}
|
Waiting for backend <LoadingIndicator />
|
||||||
<div className="lds-ring" style={{ color: 'var(--accent-font-dark)' }}>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -180,6 +180,7 @@ const Playlist = () => {
|
|||||||
<a
|
<a
|
||||||
href={`https://www.youtube.com/playlist?list=${playlist.playlist_id}`}
|
href={`https://www.youtube.com/playlist?list=${playlist.playlist_id}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
Active
|
Active
|
||||||
</a>
|
</a>
|
||||||
|
@ -779,7 +779,11 @@ const SettingsApplication = () => {
|
|||||||
<li>Make sure to contribute to this excellent project.</li>
|
<li>Make sure to contribute to this excellent project.</li>
|
||||||
<li>
|
<li>
|
||||||
More details{' '}
|
More details{' '}
|
||||||
<a target="_blank" href="https://returnyoutubedislike.com/">
|
<a
|
||||||
|
href="https://returnyoutubedislike.com/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
@ -794,7 +798,11 @@ const SettingsApplication = () => {
|
|||||||
<li>Make sure to contribute to this excellent project.</li>
|
<li>Make sure to contribute to this excellent project.</li>
|
||||||
<li>
|
<li>
|
||||||
More details{' '}
|
More details{' '}
|
||||||
<a target="_blank" href="https://sponsor.ajay.app/">
|
<a
|
||||||
|
href="https://sponsor.ajay.app/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
@ -831,7 +839,11 @@ const SettingsApplication = () => {
|
|||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Enable{' '}
|
Enable{' '}
|
||||||
<a target="_blank" href="https://returnyoutubedislike.com/">
|
<a
|
||||||
|
href="https://returnyoutubedislike.com/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
returnyoutubedislike
|
returnyoutubedislike
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@ -846,7 +858,7 @@ const SettingsApplication = () => {
|
|||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
Enable{' '}
|
Enable{' '}
|
||||||
<a href="https://sponsor.ajay.app/" target="_blank">
|
<a href="https://sponsor.ajay.app/" target="_blank" rel="noopener noreferrer">
|
||||||
Sponsorblock
|
Sponsorblock
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -279,7 +279,11 @@ const Video = () => {
|
|||||||
{video.active && (
|
{video.active && (
|
||||||
<p>
|
<p>
|
||||||
Youtube:{' '}
|
Youtube:{' '}
|
||||||
<a href={`https://www.youtube.com/watch?v=${video.youtube_id}`} target="_blank">
|
<a
|
||||||
|
href={`https://www.youtube.com/watch?v=${video.youtube_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
Active
|
Active
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
@ -458,6 +458,36 @@ button:hover {
|
|||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.player-wrapper.theater-mode {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 1000;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-main.theater-mode {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-main.theater-mode video {
|
||||||
|
max-height: 95vh;
|
||||||
|
max-width: 95vw;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.video-player {
|
.video-player {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-content: space-evenly;
|
align-content: space-evenly;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user