|
|
|
@@ -561,7 +561,7 @@ export default function Watch() {
|
|
|
|
const [currentTime, setCurrentTime] = useState(0);
|
|
|
|
const [currentTime, setCurrentTime] = useState(0);
|
|
|
|
const [queued, setQueued] = useState(null);
|
|
|
|
const [queued, setQueued] = useState(null);
|
|
|
|
const [liked, setLiked] = useState(null);
|
|
|
|
const [liked, setLiked] = useState(null);
|
|
|
|
const [rating, setRating] = useState(null);
|
|
|
|
const [disliked, setDisliked] = useState(null);
|
|
|
|
const [selectedQuality, setSelectedQuality] = useState(null);
|
|
|
|
const [selectedQuality, setSelectedQuality] = useState(null);
|
|
|
|
const [speed, setSpeed] = useState(1);
|
|
|
|
const [speed, setSpeed] = useState(1);
|
|
|
|
const [autoplay, setAutoplay] = useState(false);
|
|
|
|
const [autoplay, setAutoplay] = useState(false);
|
|
|
|
@@ -773,9 +773,9 @@ export default function Watch() {
|
|
|
|
onSuccess: (res) => { setLiked(res.data.liked); qc.invalidateQueries({ queryKey: ["liked-videos"] }); },
|
|
|
|
onSuccess: (res) => { setLiked(res.data.liked); qc.invalidateQueries({ queryKey: ["liked-videos"] }); },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const rateMut = useMutation({
|
|
|
|
const dislikeMut = useMutation({
|
|
|
|
mutationFn: (r) => rateVideo(video.id, r),
|
|
|
|
mutationFn: () => rateVideo(video.id, isDisliked ? 0 : -1),
|
|
|
|
onSuccess: (res) => setRating(res.data.rating),
|
|
|
|
onSuccess: (res) => setDisliked(res.data.rating === -1),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const handlePiP = useCallback(async () => {
|
|
|
|
const handlePiP = useCallback(async () => {
|
|
|
|
@@ -793,7 +793,7 @@ export default function Watch() {
|
|
|
|
const startAt = video?.watch_progress_seconds ?? 0;
|
|
|
|
const startAt = video?.watch_progress_seconds ?? 0;
|
|
|
|
const isQueued = queued ?? video?.queued ?? false;
|
|
|
|
const isQueued = queued ?? video?.queued ?? false;
|
|
|
|
const isLiked = liked ?? video?.liked ?? false;
|
|
|
|
const isLiked = liked ?? video?.liked ?? false;
|
|
|
|
const currentRating = rating ?? video?.rating ?? null;
|
|
|
|
const isDisliked = disliked ?? (video?.rating === -1) ?? false;
|
|
|
|
const dlComplete = dlStatus?.status === "complete" || video?.is_downloaded;
|
|
|
|
const dlComplete = dlStatus?.status === "complete" || video?.is_downloaded;
|
|
|
|
const isFollowed = followMut.isSuccess || video?.channel_followed;
|
|
|
|
const isFollowed = followMut.isSuccess || video?.channel_followed;
|
|
|
|
const subs = formatSubs(channel?.subscriber_count);
|
|
|
|
const subs = formatSubs(channel?.subscriber_count);
|
|
|
|
@@ -844,7 +844,7 @@ export default function Watch() {
|
|
|
|
<div className={theater ? "flex flex-col gap-6" : "flex flex-col xl:flex-row gap-6"}>
|
|
|
|
<div className={theater ? "flex flex-col gap-6" : "flex flex-col xl:flex-row gap-6"}>
|
|
|
|
|
|
|
|
|
|
|
|
{/* ── Left: video + info ───────────────────────────────────────────── */}
|
|
|
|
{/* ── Left: video + info ───────────────────────────────────────────── */}
|
|
|
|
<div className={theater ? "w-full flex flex-col gap-3 sm:gap-4" : "flex-1 min-w-0 flex flex-col gap-3 sm:gap-4"}>
|
|
|
|
<div className={theater ? "w-full flex flex-col gap-4 sm:gap-5" : "flex-1 min-w-0 flex flex-col gap-4 sm:gap-5"}>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Player */}
|
|
|
|
{/* Player */}
|
|
|
|
<div className={theater ? "w-full aspect-video bg-black overflow-hidden shadow-2xl" : "w-full aspect-video bg-black rounded-2xl overflow-hidden shadow-2xl"}>
|
|
|
|
<div className={theater ? "w-full aspect-video bg-black overflow-hidden shadow-2xl" : "w-full aspect-video bg-black rounded-2xl overflow-hidden shadow-2xl"}>
|
|
|
|
@@ -973,41 +973,23 @@ export default function Watch() {
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{video?.id && (
|
|
|
|
{video?.id && (
|
|
|
|
|
|
|
|
<>
|
|
|
|
<Chip active={isLiked} onClick={() => likeMut.mutate()} disabled={likeMut.isPending}>
|
|
|
|
<Chip active={isLiked} onClick={() => likeMut.mutate()} disabled={likeMut.isPending}>
|
|
|
|
<svg className="w-4 h-4" fill={isLiked ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<svg className="w-4 h-4" fill={isLiked ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
|
|
|
|
</svg>
|
|
|
|
</svg>
|
|
|
|
{isLiked ? "Liked" : "Like"}
|
|
|
|
{isLiked ? "Liked" : "Like"}
|
|
|
|
</Chip>
|
|
|
|
</Chip>
|
|
|
|
)}
|
|
|
|
<Chip active={isDisliked} onClick={() => dislikeMut.mutate()} disabled={dislikeMut.isPending} title="Not for me">
|
|
|
|
|
|
|
|
<svg className="w-4 h-4" fill={isDisliked ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
{video?.id && (
|
|
|
|
|
|
|
|
<>
|
|
|
|
|
|
|
|
<Chip
|
|
|
|
|
|
|
|
active={currentRating === 1}
|
|
|
|
|
|
|
|
onClick={() => rateMut.mutate(currentRating === 1 ? 0 : 1)}
|
|
|
|
|
|
|
|
disabled={rateMut.isPending}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<svg className="w-4 h-4" fill={currentRating === 1 ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z"/>
|
|
|
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 22H4a2 2 0 01-2-2v-7a2 2 0 012-2h3"/>
|
|
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
|
|
Good
|
|
|
|
|
|
|
|
</Chip>
|
|
|
|
|
|
|
|
<Chip
|
|
|
|
|
|
|
|
active={currentRating === -1}
|
|
|
|
|
|
|
|
onClick={() => rateMut.mutate(currentRating === -1 ? 0 : -1)}
|
|
|
|
|
|
|
|
disabled={rateMut.isPending}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<svg className="w-4 h-4" fill={currentRating === -1 ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z"/>
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z"/>
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 2h2.67A2.31 2.31 0 0122 4v7a2.31 2.31 0 01-2.33 2H17"/>
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 2h2.67A2.31 2.31 0 0122 4v7a2.31 2.31 0 01-2.33 2H17"/>
|
|
|
|
</svg>
|
|
|
|
</svg>
|
|
|
|
Not for me
|
|
|
|
|
|
|
|
</Chip>
|
|
|
|
</Chip>
|
|
|
|
</>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{video?.id && (
|
|
|
|
{video?.id && (
|
|
|
|
<Chip active={isQueued} onClick={() => queueMut.mutate()} disabled={queueMut.isPending}>
|
|
|
|
<Chip active={isQueued} onClick={() => queueMut.mutate()} disabled={queueMut.isPending}>
|
|
|
|
<svg className="w-4 h-4" fill={isQueued ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<svg className="w-4 h-4" fill={isQueued ? "currentColor" : "none"} stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
@@ -1018,16 +1000,15 @@ export default function Watch() {
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{fileReady && document.pictureInPictureEnabled && (
|
|
|
|
{fileReady && document.pictureInPictureEnabled && (
|
|
|
|
<Chip onClick={handlePiP}>
|
|
|
|
<Chip onClick={handlePiP} title="Picture in picture">
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<rect x="2" y="4" width="20" height="16" rx="2" strokeWidth="2"/>
|
|
|
|
<rect x="2" y="4" width="20" height="16" rx="2" strokeWidth="2"/>
|
|
|
|
<rect x="12" y="12" width="8" height="6" rx="1.5" strokeWidth="2"/>
|
|
|
|
<rect x="12" y="12" width="8" height="6" rx="1.5" strokeWidth="2"/>
|
|
|
|
</svg>
|
|
|
|
</svg>
|
|
|
|
Mini
|
|
|
|
|
|
|
|
</Chip>
|
|
|
|
</Chip>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Chip active={theater} onClick={() => setTheater(t => !t)}>
|
|
|
|
<Chip active={theater} onClick={() => setTheater(t => !t)} title={theater ? "Exit theater" : "Theater mode"}>
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
{theater ? (
|
|
|
|
{theater ? (
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 9L4 4m0 5V4h5M15 9l5-5m0 5V4h-5M9 15l-5 5m0-5v5h5M15 15l5 5m0-5v5h-5" />
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 9L4 4m0 5V4h5M15 9l5-5m0 5V4h-5M9 15l-5 5m0-5v5h5M15 15l5 5m0-5v5h-5" />
|
|
|
|
@@ -1035,7 +1016,6 @@ export default function Watch() {
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 8V5a1 1 0 011-1h3M4 16v3a1 1 0 001 1h3m10-4v3a1 1 0 01-1 1h-3M20 8V5a1 1 0 00-1-1h-3" />
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 8V5a1 1 0 011-1h3M4 16v3a1 1 0 001 1h3m10-4v3a1 1 0 01-1 1h-3M20 8V5a1 1 0 00-1-1h-3" />
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
</svg>
|
|
|
|
</svg>
|
|
|
|
Theater
|
|
|
|
|
|
|
|
</Chip>
|
|
|
|
</Chip>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|