mirror of
https://github.com/neosubhamoy/pytubepp-helper.git
synced 2026-02-04 11:22:22 +05:30
(feat): added support for common rhel (rpm) based linux distros
This commit is contained in:
@@ -18,11 +18,21 @@
|
|||||||
"sidecar": true,
|
"sidecar": true,
|
||||||
"open": true,
|
"open": true,
|
||||||
"scope": [
|
"scope": [
|
||||||
|
{
|
||||||
|
"name": "detect-distro",
|
||||||
|
"cmd": "grep",
|
||||||
|
"args": ["^ID=", "/etc/os-release"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "is-apt-installed",
|
"name": "is-apt-installed",
|
||||||
"cmd": "apt",
|
"cmd": "apt",
|
||||||
"args": ["--version"]
|
"args": ["--version"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "is-dnf-installed",
|
||||||
|
"cmd": "dnf",
|
||||||
|
"args": ["--version"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "is-python-installed",
|
"name": "is-python-installed",
|
||||||
"cmd": "python3",
|
"cmd": "python3",
|
||||||
@@ -99,6 +109,20 @@
|
|||||||
"/usr/bin/pytubepp-helper-autostart": "./target/release/pytubepp-helper-autostart",
|
"/usr/bin/pytubepp-helper-autostart": "./target/release/pytubepp-helper-autostart",
|
||||||
"/etc/xdg/autostart/pytubepp-helper-autostart.desktop": "./autostart/pytubepp-helper-autostart.desktop"
|
"/etc/xdg/autostart/pytubepp-helper-autostart.desktop": "./autostart/pytubepp-helper-autostart.desktop"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"rpm": {
|
||||||
|
"epoch": 0,
|
||||||
|
"release": "1",
|
||||||
|
"license": "MIT",
|
||||||
|
"depends": ["python3-pip", "ffmpeg-free"],
|
||||||
|
"files": {
|
||||||
|
"/etc/opt/chrome/native-messaging-hosts/com.neosubhamoy.pytubepp.helper.json": "./msghost-manifest/chrome/com.neosubhamoy.pytubepp.helper.json",
|
||||||
|
"/etc/chromium/native-messaging-hosts/com.neosubhamoy.pytubepp.helper.json": "./msghost-manifest/chrome/com.neosubhamoy.pytubepp.helper.json",
|
||||||
|
"/usr/lib/mozilla/native-messaging-hosts/com.neosubhamoy.pytubepp.helper.json": "./msghost-manifest/firefox/com.neosubhamoy.pytubepp.helper.json",
|
||||||
|
"/usr/bin/pytubepp-helper-msghost": "./target/release/pytubepp-helper-msghost",
|
||||||
|
"/usr/bin/pytubepp-helper-autostart": "./target/release/pytubepp-helper-autostart",
|
||||||
|
"/etc/xdg/autostart/pytubepp-helper-autostart.desktop": "./autostart/pytubepp-helper-autostart.desktop"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systemTray": {
|
"systemTray": {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { listen } from '@tauri-apps/api/event';
|
|||||||
import { appWindow } from '@tauri-apps/api/window';
|
import { appWindow } from '@tauri-apps/api/window';
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
import { InstalledPrograms, WebSocketMessage, } from "./types";
|
import { InstalledPrograms, WebSocketMessage, } from "./types";
|
||||||
import { compareVersions, extract_version, is_installed, sendStreamInfo } from "./lib/utils";
|
import { compareVersions, extractVersion, isInstalled, sendStreamInfo, extractDistroId, detectDistro, detectDistroBase } from "./lib/utils";
|
||||||
import { CircleCheck, TriangleAlert, CircleAlert } from 'lucide-react';
|
import { CircleCheck, TriangleAlert, CircleAlert } from 'lucide-react';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -20,11 +20,17 @@ function App() {
|
|||||||
appWindow.onCloseRequested(handleCloseRequested);
|
appWindow.onCloseRequested(handleCloseRequested);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [distroId, setDistroId] = useState<string | null>(null)
|
||||||
|
const [distroBase, setDistroBase] = useState<string | null>(null)
|
||||||
const [installedPrograms, setInstalledPrograms] = useState<InstalledPrograms>({
|
const [installedPrograms, setInstalledPrograms] = useState<InstalledPrograms>({
|
||||||
apt: {
|
apt: {
|
||||||
installed: false,
|
installed: false,
|
||||||
version: null,
|
version: null,
|
||||||
},
|
},
|
||||||
|
dnf: {
|
||||||
|
installed: false,
|
||||||
|
version: null,
|
||||||
|
},
|
||||||
python: {
|
python: {
|
||||||
installed: false,
|
installed: false,
|
||||||
version: null,
|
version: null,
|
||||||
@@ -71,56 +77,71 @@ function App() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function check_all_programs() {
|
function checkAllPrograms() {
|
||||||
is_installed('apt', '--version').then((result) => {
|
isInstalled('apt', '--version').then((result) => {
|
||||||
setInstalledPrograms((prevState) => ({
|
setInstalledPrograms((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
apt: {
|
apt: {
|
||||||
installed: result.installed,
|
installed: result.installed,
|
||||||
version: result.output ? extract_version(result.output) : null,
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
is_installed('python', '--version').then((result) => {
|
isInstalled('dnf', '--version').then((result) => {
|
||||||
|
setInstalledPrograms((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
dnf: {
|
||||||
|
installed: result.installed,
|
||||||
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
isInstalled('python', '--version').then((result) => {
|
||||||
setInstalledPrograms((prevState) => ({
|
setInstalledPrograms((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
python: {
|
python: {
|
||||||
installed: result.installed,
|
installed: result.installed,
|
||||||
version: result.output ? extract_version(result.output) : null,
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
is_installed('pip', '--version').then((result) => {
|
isInstalled('pip', '--version').then((result) => {
|
||||||
setInstalledPrograms((prevState) => ({
|
setInstalledPrograms((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
pip: {
|
pip: {
|
||||||
installed: result.installed,
|
installed: result.installed,
|
||||||
version: result.output ? extract_version(result.output) : null,
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
is_installed('ffmpeg', '-version').then((result) => {
|
isInstalled('ffmpeg', '-version').then((result) => {
|
||||||
setInstalledPrograms((prevState) => ({
|
setInstalledPrograms((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
ffmpeg: {
|
ffmpeg: {
|
||||||
installed: result.installed,
|
installed: result.installed,
|
||||||
version: result.output ? extract_version(result.output) : null,
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
is_installed('pytubepp', '--version').then((result) => {
|
isInstalled('pytubepp', '--version').then((result) => {
|
||||||
setInstalledPrograms((prevState) => ({
|
setInstalledPrograms((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
pytubepp: {
|
pytubepp: {
|
||||||
installed: result.installed,
|
installed: result.installed,
|
||||||
version: result.output ? extract_version(result.output) : null,
|
version: result.output ? extractVersion(result.output) : null,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
check_all_programs();
|
checkAllPrograms();
|
||||||
|
detectDistro().then((result) => {
|
||||||
|
if(result) {
|
||||||
|
setDistroId(extractDistroId(result))
|
||||||
|
setDistroBase(detectDistroBase(extractDistroId(result)))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
, []);
|
, []);
|
||||||
|
|
||||||
@@ -129,8 +150,9 @@ function App() {
|
|||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="topbar flex justify-between items-center mt-5 mx-3">
|
<div className="topbar flex justify-between items-center mt-5 mx-3">
|
||||||
<h1 className="text-xl font-bold">PytubePP Helper</h1>
|
<h1 className="text-xl font-bold">PytubePP Helper</h1>
|
||||||
<Button size="sm" onClick={check_all_programs}>Refresh</Button>
|
<Button size="sm" onClick={checkAllPrograms}>Refresh</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{ distroId && distroBase && distroBase === 'debian' ?
|
||||||
<div className="programstats mt-5 mx-3">
|
<div className="programstats mt-5 mx-3">
|
||||||
<div className="programitem flex items-center justify-between">
|
<div className="programitem flex items-center justify-between">
|
||||||
<p><b>Python:</b> {installedPrograms.python.installed ? 'installed' : 'not installed'} {installedPrograms.python.version ? `(${installedPrograms.python.version})` : ''}</p>
|
<p><b>Python:</b> {installedPrograms.python.installed ? 'installed' : 'not installed'} {installedPrograms.python.version ? `(${installedPrograms.python.version})` : ''}</p>
|
||||||
@@ -172,6 +194,59 @@ function App() {
|
|||||||
</Alert>
|
</Alert>
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
|
: distroId && distroBase && distroBase === 'rhel' ?
|
||||||
|
<div className="programstats mt-5 mx-3">
|
||||||
|
<div className="programitem flex items-center justify-between">
|
||||||
|
<p><b>Python:</b> {installedPrograms.python.installed ? 'installed' : 'not installed'} {installedPrograms.python.version ? `(${installedPrograms.python.version})` : ''}</p>
|
||||||
|
{installedPrograms.python.installed ? installedPrograms.python.version ? compareVersions(installedPrograms.python.version, '3.8') < 0 ? <TriangleAlert className="w-5 h-5 my-2 text-orange-400"/> : <CircleCheck className="w-5 h-5 my-2 text-green-400"/> : installedPrograms.dnf.installed ? <Button variant="link" className="text-blue-600 px-0" onClick={async () => { await invoke('install_program', {icommand: 'sudo dnf install python3.12 -y'})}}>install</Button> : <TriangleAlert className="w-5 h-5 my-2 text-orange-400"/> : null}
|
||||||
|
</div>
|
||||||
|
<div className="programitem flex items-center justify-between">
|
||||||
|
<p><b>FFmpeg:</b> {installedPrograms.ffmpeg.installed ? 'installed' : 'not installed'} {installedPrograms.ffmpeg.version ? `(${installedPrograms.ffmpeg.version})` : ''}</p>
|
||||||
|
{installedPrograms.ffmpeg.installed ? <CircleCheck className="w-5 h-5 my-2 text-green-400"/> : installedPrograms.dnf.installed ? <Button variant="link" className="text-blue-600 px-0" onClick={async () => { await invoke('install_program', {icommand: 'sudo dnf install ffmpeg-free -y'})}}>install</Button> : null}
|
||||||
|
</div>
|
||||||
|
<div className="programitem flex items-center justify-between">
|
||||||
|
<p><b>PytubePP:</b> {installedPrograms.pytubepp.installed ? 'installed' : 'not installed'} {installedPrograms.pytubepp.version ? `(${installedPrograms.pytubepp.version})` : ''}</p>
|
||||||
|
{installedPrograms.pytubepp.installed ? <CircleCheck className="w-5 h-5 my-2 text-green-400"/> : installedPrograms.pip.installed ? <Button variant="link" className="text-blue-600 px-0" onClick={async () => { await invoke('install_program', {icommand: 'pip install pytubepp'})}}>install</Button> : null}
|
||||||
|
</div>
|
||||||
|
{(!installedPrograms.dnf.installed && (!installedPrograms.python.installed || !installedPrograms.ffmpeg.installed)) ?
|
||||||
|
<Alert className="mt-5" variant="destructive">
|
||||||
|
<CircleAlert className="h-5 w-5" />
|
||||||
|
<AlertTitle>DNF Not Found</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
DNF is required to install necessary debian packages. Please install it manually for your distro.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
: null}
|
||||||
|
{(!installedPrograms.pip.installed && !installedPrograms.pytubepp.installed) ?
|
||||||
|
<Alert className="mt-5" variant="destructive">
|
||||||
|
<CircleAlert className="h-5 w-5" />
|
||||||
|
<AlertTitle>PIP Not Found</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
PIP is required to install necessary python packages. Please install it now to continue: <Button variant="link" className="text-blue-600 p-0" onClick={async () => { await invoke('install_program', {icommand: 'sudo dnf install python3-pip -y'})}}>install</Button>
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
: null}
|
||||||
|
{(installedPrograms.python.installed && installedPrograms.ffmpeg.installed && installedPrograms.pytubepp.installed) ?
|
||||||
|
<Alert className="mt-5">
|
||||||
|
<CircleCheck className="h-5 w-5" />
|
||||||
|
<AlertTitle>Ready</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
Everything looks ok! You can close this window now. Make sure it's always running in the background.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="programstats mt-5 mx-3">
|
||||||
|
<Alert className="mt-5" variant="destructive">
|
||||||
|
<CircleAlert className="h-5 w-5" />
|
||||||
|
<AlertTitle>Unsupported Distro</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
Sorry, your linux distro is currently not supported. If you think this is just a mistake or you want to request us to add support for your distro you can create a github issue <a className="underline" href="https://github.com/neosubhamoy/pytubepp-helper/issues" target="_blank">here</a>.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Command } from '@tauri-apps/api/shell';
|
|||||||
import { Stream } from "@/types";
|
import { Stream } from "@/types";
|
||||||
import { invoke } from "@tauri-apps/api";
|
import { invoke } from "@tauri-apps/api";
|
||||||
|
|
||||||
export function extract_xml(input: string): string[] {
|
export function extractXml(input: string): string[] {
|
||||||
const regex = /<Stream: [^>]+>/g;
|
const regex = /<Stream: [^>]+>/g;
|
||||||
const matches = input.match(regex);
|
const matches = input.match(regex);
|
||||||
return matches ? matches : [];
|
return matches ? matches : [];
|
||||||
@@ -27,7 +27,7 @@ function parseAttributes(attributesString: string): Partial<Stream> {
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convert_xml_to_json(xmlStrings: string[]): Stream[] {
|
export function convertXmlToJson(xmlStrings: string[]): Stream[] {
|
||||||
return xmlStrings
|
return xmlStrings
|
||||||
.map(xmlString => {
|
.map(xmlString => {
|
||||||
const attributesString = xmlString.replace('<Stream: ', '').replace('>', '');
|
const attributesString = xmlString.replace('<Stream: ', '').replace('>', '');
|
||||||
@@ -40,7 +40,7 @@ export function cn(...inputs: ClassValue[]) {
|
|||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function is_installed(program: string, arg: string): Promise<{ installed: boolean, output: string | null }> {
|
export async function isInstalled(program: string, arg: string): Promise<{ installed: boolean, output: string | null }> {
|
||||||
try{
|
try{
|
||||||
const output = await new Command('is-' + program + '-installed', [arg]).execute();
|
const output = await new Command('is-' + program + '-installed', [arg]).execute();
|
||||||
if (output.code === 0) {
|
if (output.code === 0) {
|
||||||
@@ -54,13 +54,42 @@ export async function is_installed(program: string, arg: string): Promise<{ inst
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extract_version(output: string): string | null {
|
export async function detectDistro(): Promise<string | null> {
|
||||||
|
try{
|
||||||
|
const output = await new Command('detect-distro', ['^ID=', '/etc/os-release']).execute();
|
||||||
|
if (output.code === 0) {
|
||||||
|
return output.stdout;
|
||||||
|
} else {
|
||||||
|
return output.stdout;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectDistroBase(distro: string | null): string | null{
|
||||||
|
if(distro) {
|
||||||
|
if(['debian', 'ubuntu', 'pop', 'kali'].includes(distro)) {
|
||||||
|
return 'debian';
|
||||||
|
} else if (['rhel', 'fedora', 'centos', 'rocky'].includes(distro)) {
|
||||||
|
return 'rhel';
|
||||||
|
} else {
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractVersion(output: string): string | null {
|
||||||
const versionPatterns = [
|
const versionPatterns = [
|
||||||
/ffmpeg version (\d+\.\d+)/, // Pattern for ffmpeg
|
/ffmpeg version (\d+\.\d+)/, // Pattern for ffmpeg
|
||||||
/Python (\d+\.\d+\.\d+)/, // Pattern for Python
|
/Python (\d+\.\d+\.\d+)/, // Pattern for Python
|
||||||
/pytubefix (\d+\.\d+\.\d+)/, // Pattern for pytubefix
|
/pytubefix (\d+\.\d+\.\d+)/, // Pattern for pytubefix
|
||||||
/pytubepp (\d+\.\d+\.\d+)/, // Pattern for pytubepp
|
/pytubepp (\d+\.\d+\.\d+)/, // Pattern for pytubepp
|
||||||
/apt (\d+\.\d+\.\d+)/, // Pattern for apt
|
/apt (\d+\.\d+\.\d+)/, // Pattern for apt
|
||||||
|
/(\d+\.\d+\.\d+)/, // Pattern for dnf
|
||||||
/pip (\d+\.\d+)/, // Pattern for pip
|
/pip (\d+\.\d+)/, // Pattern for pip
|
||||||
|
|
||||||
];
|
];
|
||||||
@@ -73,6 +102,12 @@ export function extract_version(output: string): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function extractDistroId(input: string): string | null {
|
||||||
|
const regex = /ID=([a-zA-Z]+)/;
|
||||||
|
const match = input.match(regex);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
export async function sendStreamInfo(url: string) {
|
export async function sendStreamInfo(url: string) {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -81,7 +116,7 @@ export async function sendStreamInfo(url: string) {
|
|||||||
console.log(output.stdout);
|
console.log(output.stdout);
|
||||||
const sendStreamData = async () => {
|
const sendStreamData = async () => {
|
||||||
try {
|
try {
|
||||||
const streamsstr = JSON.stringify(convert_xml_to_json(extract_xml(output.stdout)))
|
const streamsstr = JSON.stringify(convertXmlToJson(extractXml(output.stdout)))
|
||||||
await invoke('receive_frontend_response', { response: streamsstr });
|
await invoke('receive_frontend_response', { response: streamsstr });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ export interface InstalledPrograms {
|
|||||||
installed: boolean;
|
installed: boolean;
|
||||||
version: string | null;
|
version: string | null;
|
||||||
};
|
};
|
||||||
|
dnf: {
|
||||||
|
installed: boolean;
|
||||||
|
version: string | null;
|
||||||
|
}
|
||||||
python: {
|
python: {
|
||||||
installed: boolean;
|
installed: boolean;
|
||||||
version: string | null;
|
version: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user