diff --git a/renderer/hooks/useLoadFiles.tsx b/renderer/hooks/useLoadFiles.tsx
index f316e11..acee2d5 100644
--- a/renderer/hooks/useLoadFiles.tsx
+++ b/renderer/hooks/useLoadFiles.tsx
@@ -1,10 +1,15 @@
import {useCallback, useEffect, useState} from 'react';
-import {setGlobalSubtitleId, SubtitleContainer} from "../components/DataStructures";
+import {
+ convertSubtitlesToEntries,
+ setGlobalSubtitleId,
+ SubtitleContainer
+} from "../components/DataStructures";
import {randomUUID} from "crypto";
import {TOAST_TIMEOUT} from "../components/Toast";
-import {isSubtitle, isVideo} from "../utils/utils";
+import {extractVideoId, isLocalPath, isSubtitle, isVideo, isYoutube} from "../utils/utils";
import {findPositionDeltaInFolder} from "../utils/folderUtils";
import {useAsyncAwaitQueue} from "./useAsyncAwaitQueue";
+import {ipcRenderer} from 'electron';
const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, setSecondarySub, tokenizeMiteiru, setEnableSeeker, changeTimeTo, player) => {
const [videoSrc, setVideoSrc] = useState({src: '', type: '', path: ''});
@@ -16,12 +21,29 @@ const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, set
const currentHash = Symbol();
await queue.wait(currentHash);
let currentPath = acceptedFiles[0].path;
- currentPath = currentPath.replaceAll('\\', '/')
- let pathUri = currentPath;
- if (process.platform === 'win32') {
- pathUri = '/' + currentPath;
+ let pathUri;
+ if (isLocalPath(currentPath)) {
+ currentPath = currentPath.replaceAll('\\', '/')
+ pathUri = currentPath;
+ if (process.platform === 'win32') {
+ pathUri = '/' + currentPath;
+ }
+ }
+ if (isVideo(currentPath) || isYoutube(currentPath)) {
+ const draggedVideo = isYoutube(currentPath) ? {
+ type: 'video/youtube',
+ src: currentPath,
+ path: currentPath
+ } : {
+ type: 'video/webm',
+ src: `miteiru://${pathUri}`,
+ path: pathUri
+ };
+ setVideoSrc(draggedVideo);
+ resetSub(setPrimarySub)
+ resetSub(setSecondarySub)
}
- if (isSubtitle(currentPath)) {
+ if (isSubtitle(currentPath) || isYoutube(currentPath)) {
setToastInfo({
message: 'Loading subtitle, please wait!',
update: randomUUID()
@@ -36,7 +58,8 @@ const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, set
type: 'text/plain',
src: `${currentPath}`
};
- SubtitleContainer.create(draggedSubtitle.src).then(tmpSub => {
+ const subLoader = (tmpSub, mustMatch = null) => {
+ if(mustMatch !== null && tmpSub.language !== mustMatch) return;
clearInterval(toastSetter);
if (tmpSub.language === "JP") {
setPrimarySub(tmpSub);
@@ -59,20 +82,26 @@ const useLoadFiles = (setToastInfo, primarySub, setPrimarySub, secondarySub, set
clearInterval(toastSetter);
})
}
- });
- } else if (isVideo(currentPath)) {
- const draggedVideo = {
- type: 'video/webm',
- src: `miteiru://${pathUri}`,
- path: pathUri
};
- setVideoSrc(draggedVideo);
- resetSub(setPrimarySub)
- resetSub(setSecondarySub)
+ if (isYoutube(currentPath)) {
+ ipcRenderer.invoke("getYoutubeSubtitle", extractVideoId(currentPath), "en").then(entries => {
+ entries = convertSubtitlesToEntries(entries)
+ const tmpSub = SubtitleContainer.createFromArrayEntries(null, entries)
+ subLoader(tmpSub, "EN");
+ })
+ ipcRenderer.invoke("getYoutubeSubtitle", extractVideoId(currentPath), "ja").then(entries => {
+ entries = convertSubtitlesToEntries(entries)
+ const tmpSub = SubtitleContainer.createFromArrayEntries(null, entries)
+ subLoader(tmpSub, "JP");
+ })
+ } else {
+ SubtitleContainer.create(draggedSubtitle.src).then(subLoader);
+ }
}
await queue.end(currentHash);
}, [tokenizeMiteiru]);
const onVideoChangeHandler = useCallback(async (delta: number = 1) => {
+ if (!isLocalPath(videoSrc.path)) return;
if (videoSrc.path) {
const nextVideo = findPositionDeltaInFolder(videoSrc.path, delta);
if (nextVideo !== '') {
diff --git a/renderer/pages/video.tsx b/renderer/pages/video.tsx
index 15f9d57..db197f8 100644
--- a/renderer/pages/video.tsx
+++ b/renderer/pages/video.tsx
@@ -74,8 +74,21 @@ function Video() {
{
export const isVideo = (path) => {
return isArrayEndsWithMatcher(path, videoConstants.supportedVideoFormats);
}
+export const isYoutube = (url) => {
+ // Regular expression to match YouTube URLs.
+ // It matches following formats:
+ // - www.youtube.com/watch?v=VIDEO_ID
+ // - m.youtube.com/watch?v=VIDEO_ID
+ // - youtube.com/watch?v=VIDEO_ID
+ // - www.youtube.com/v/VIDEO_ID
+ // - http://youtu.be/VIDEO_ID
+ // - youtube.com/embed/VIDEO_ID
+ // - https://www.youtube.com/shorts/VIDEO_ID
+ const pattern = /^(http(s)?:\/\/)?((w){3}.)?youtu(be|.be)?(\.com)?\/(watch|embed|v|shorts)?(\?v=)?(\?embed)?\/?(\S+)?$/;
+ return pattern.test(url);
+}
+
+export const isDomainUri = (url) => {
+ try {
+ const parsedUrl = new URL(url);
+ return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
+ } catch (e) {
+ return false;
+ }
+}
+
+export const isLocalPath = (url) => {
+ // If it's not a valid URL, it might be a local path
+ return !isDomainUri(url);
+}
+
export const isSubtitle = (path) => {
return isArrayEndsWithMatcher(path, videoConstants.supportedSubtitleFormats);
}
+export const extractVideoId = (url) => {
+ const regex = /(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/(watch\?v=)?([^&]+)/;
+ const results = regex.exec(url);
+ if (!results) {
+ return null;
+ }
+ return results[5];
+}
+
const getFormattedNameFromPath = (path) => {
const pathList = path.split('/');
return path ? (' - ' + pathList[pathList.length - 1]) : ''