mirror of
https://github.com/jely2002/youtube-dl-gui.git
synced 2021-11-01 22:46:21 +03:00
Automatically detect what python install to use on linux systems
This commit is contained in:
21
modules/DetectPython.js
Normal file
21
modules/DetectPython.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const execa = require("execa");
|
||||
|
||||
class DetectPython {
|
||||
async test(command) {
|
||||
try {
|
||||
await execa(command, ['--version']);
|
||||
return true;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async detect() {
|
||||
if(await this.test("python") === true) return "python";
|
||||
else if(await this.test("python3") === true) return "python3";
|
||||
else if(await this.test("python2") === true) return "python2";
|
||||
else return "python";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetectPython;
|
||||
@@ -2,6 +2,7 @@ const Bottleneck = require("bottleneck");
|
||||
const Filepaths = require("./Filepaths");
|
||||
const Settings = require("./Settings");
|
||||
const Analytics = require("./Analytics");
|
||||
const DetectPython = require("./DetectPython");
|
||||
const fs = require("fs").promises;
|
||||
|
||||
class Environment {
|
||||
@@ -36,6 +37,12 @@ class Environment {
|
||||
this.settings.save();
|
||||
})
|
||||
}
|
||||
if(process.platform === "linux") {
|
||||
const pythonDetect = new DetectPython();
|
||||
this.pythonCommand = await pythonDetect.detect();
|
||||
} else {
|
||||
this.pythonCommand = "python";
|
||||
}
|
||||
this.analytics = new Analytics(this.app.getVersion(), this.paths, this.settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,17 @@ class Query {
|
||||
args.push(this.environment.settings.cookiePath);
|
||||
}
|
||||
args.push(url) //Url must always be added as the final argument
|
||||
|
||||
let command = this.environment.paths.ytdl; //Set the command to be executed
|
||||
|
||||
if(this.environment.pythonCommand !== "python") { //If standard python is not available use another install if detected
|
||||
args.unshift(this.environment.paths.ytdl);
|
||||
command = this.environment.pythonCommand;
|
||||
}
|
||||
if(cb == null) {
|
||||
//Return the data after the query has completed fully.
|
||||
try {
|
||||
const {stdout} = await execa(this.environment.paths.ytdl, args);
|
||||
const {stdout} = await execa(command, args);
|
||||
return stdout
|
||||
} catch(e) {
|
||||
this.environment.errorHandler.checkError(e.stderr, this.identifier);
|
||||
@@ -38,7 +45,7 @@ class Query {
|
||||
//Return data while the query is running (live)
|
||||
//Return "close" when the query has finished
|
||||
return await new Promise((resolve) => {
|
||||
this.process = execa(this.environment.paths.ytdl, args);
|
||||
this.process = execa(command, args);
|
||||
this.process.stdout.setEncoding('utf8');
|
||||
this.process.stdout.on('data', (data) => {
|
||||
cb(data.toString());
|
||||
|
||||
50
tests/DetectPython.test.js
Normal file
50
tests/DetectPython.test.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const execa = require('execa');
|
||||
const DetectPython = require("../modules/DetectPython");
|
||||
|
||||
jest.mock('execa');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.doMock('execa', () => {
|
||||
const originalModule = jest.requireActual('execa')
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
execa: jest.fn()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('test', () => {
|
||||
it('returns true when successful', () => {
|
||||
const instance = new DetectPython();
|
||||
execa.mockResolvedValue("test output");
|
||||
expect(instance.test()).resolves.toBeTruthy();
|
||||
});
|
||||
it('returns false on error', () => {
|
||||
const instance = new DetectPython();
|
||||
execa.mockImplementation(() => {
|
||||
throw new Error("ENOENT");
|
||||
});
|
||||
expect(instance.test()).resolves.toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('detect', () => {
|
||||
it("Returns python if all tests fail", () => {
|
||||
const instance = new DetectPython();
|
||||
jest.spyOn(instance, 'test').mockResolvedValue(false);
|
||||
expect(instance.detect()).resolves.toEqual("python");
|
||||
});
|
||||
it("Returns the command of the first completed test", async () => {
|
||||
const commands = ["python", "python3", "python2"];
|
||||
for(const command of commands) {
|
||||
const instance = new DetectPython();
|
||||
jest.spyOn(instance, 'test').mockImplementation((cmd) => {
|
||||
return cmd === command;
|
||||
})
|
||||
const result = await instance.detect();
|
||||
expect(result).toEqual(command);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -25,7 +25,7 @@ describe('ytdl Query', () => {
|
||||
it('adds a random user agent when this setting is enabled', () => {
|
||||
UserAgent.prototype.toString = jest.fn().mockReturnValue("agent");
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock, "python");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(UserAgent.prototype.toString).toBeCalledTimes(1);
|
||||
expect(execa.mock.calls[0][1]).toContain("--user-agent");
|
||||
@@ -34,22 +34,30 @@ describe('ytdl Query', () => {
|
||||
});
|
||||
it('adds the cookies argument when specified in settings', () => {
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(false, "a/path/to/cookies.txt", errorHandlerMock);
|
||||
const instance = instanceBuilder(false, "a/path/to/cookies.txt", errorHandlerMock, "python");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(execa.mock.calls[0][1]).toContain("--cookies");
|
||||
expect(execa.mock.calls[0][1]).toContain("a/path/to/cookies.txt");
|
||||
});
|
||||
});
|
||||
it('uses the detected python command', () => {
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock, "python3");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(execa.mock.calls[0][0]).toEqual("python3");
|
||||
expect(execa.mock.calls[0][1][0]).toEqual("a/path/to/ytdl");
|
||||
});
|
||||
});
|
||||
it('adds the url as final argument', () => {
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(execa.mock.calls[0][1]).toContain("https://url.link");
|
||||
expect(execa.mock.calls[0][1][execa.mock.calls[0][1].length - 1]).toContain("https://url.link");
|
||||
});
|
||||
})
|
||||
it('adds the no-cache-dir as argument', () => {
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(execa.mock.calls[0][1]).toContain("--no-cache-dir");
|
||||
});
|
||||
@@ -62,7 +70,7 @@ describe('Query with live callback', () => {
|
||||
execa.mockReturnValue(mock)
|
||||
const errorHandlerMock = jest.fn();
|
||||
const callbackMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
|
||||
const result = instance.start("https://url.link", [], callbackMock);
|
||||
setTimeout(() => {
|
||||
instance.stop();
|
||||
@@ -76,7 +84,7 @@ describe('Query with live callback', () => {
|
||||
console.error = jest.fn();
|
||||
const errorHandlerMock = jest.fn();
|
||||
const callbackMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
|
||||
const result = instance.start("https://url.link", [], callbackMock);
|
||||
setTimeout(() => {
|
||||
stderr.emit("data", "test-error");
|
||||
@@ -91,7 +99,7 @@ describe('Query with live callback', () => {
|
||||
const [stdout, stderr, mock] = execaMockBuilder(false);
|
||||
execa.mockReturnValue(mock)
|
||||
const callbackMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, jest.fn());
|
||||
const instance = instanceBuilder(false, null, jest.fn(), "python");
|
||||
const result = instance.start("https://url.link", [], callbackMock);
|
||||
setTimeout(() => {
|
||||
stdout.emit("close");
|
||||
@@ -103,7 +111,7 @@ describe('Query with live callback', () => {
|
||||
const [stdout, stderr, mock] = execaMockBuilder(false);
|
||||
execa.mockReturnValue(mock);
|
||||
const callbackMock = jest.fn();
|
||||
const instance = instanceBuilder(false, null, jest.fn());
|
||||
const instance = instanceBuilder(false, null, jest.fn(), "python");
|
||||
const result = instance.start("https://url.link", [], callbackMock);
|
||||
setTimeout(() => {
|
||||
stdout.emit("data", "test-data");
|
||||
@@ -120,21 +128,21 @@ describe('Query without callback', () => {
|
||||
it('Returns the data from the execa call', async () => {
|
||||
execa.mockResolvedValue({stdout: "fake-data"});
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock, "python");
|
||||
const result = instance.start("https://url.link", [], null)
|
||||
await expect(result).resolves.toEqual("fake-data");
|
||||
});
|
||||
it('Returns a stringified empty object on error', async () => {
|
||||
execa.mockResolvedValue(null);
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock, "python");
|
||||
const result = instance.start("https://url.link", [], null)
|
||||
await expect(result).resolves.toEqual("{}");
|
||||
});
|
||||
it('Checks the error on error', () => {
|
||||
execa.mockResolvedValue(null);
|
||||
const errorHandlerMock = jest.fn();
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock);
|
||||
const instance = instanceBuilder(true, null, errorHandlerMock, "python");
|
||||
return instance.start("https://url.link", [], null).then(() => {
|
||||
expect(errorHandlerMock).toBeCalledTimes(1);
|
||||
});
|
||||
@@ -148,6 +156,6 @@ function execaMockBuilder(killed) {
|
||||
return [stdout, stderr, mock];
|
||||
}
|
||||
|
||||
function instanceBuilder(spoofUserAgent, cookiePath, errorHandlerMock) {
|
||||
return new Query({errorHandler: {checkError: errorHandlerMock}, paths: {ytdl: "a/path/to/ytdl"}, settings: {cookiePath: cookiePath, spoofUserAgent: spoofUserAgent}}, "test__id");
|
||||
function instanceBuilder(spoofUserAgent, cookiePath, errorHandlerMock, pythonCommand) {
|
||||
return new Query({pythonCommand: pythonCommand, errorHandler: {checkError: errorHandlerMock}, paths: {ytdl: "a/path/to/ytdl"}, settings: {cookiePath: cookiePath, spoofUserAgent: spoofUserAgent}}, "test__id");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user