Skip to content

Commit

Permalink
feat: add setting to use empty user agent #213
Browse files Browse the repository at this point in the history
  • Loading branch information
jely2002 committed Oct 17, 2021
1 parent b288ff3 commit 84e82b7
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 30 deletions.
11 changes: 5 additions & 6 deletions modules/persistence/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const fs = require("fs").promises;
class Settings {
constructor(
paths, env, outputFormat, audioOutputFormat, downloadPath,
proxy, rateLimit, autoFillClipboard, noPlaylist, globalShortcut, spoofUserAgent,
proxy, rateLimit, autoFillClipboard, noPlaylist, globalShortcut, userAgent,
validateCertificate, enableEncoding, taskList, nameFormat, nameFormatMode,
sizeMode, splitMode, maxConcurrent, updateBinary, downloadType, updateApplication, cookiePath,
statSend, downloadMetadata, downloadThumbnail, keepUnmerged, calculateTotalSize, theme
Expand All @@ -20,7 +20,7 @@ class Settings {
this.autoFillClipboard = autoFillClipboard == null ? true : autoFillClipboard;
this.noPlaylist = noPlaylist == null ? false : noPlaylist;
this.globalShortcut = globalShortcut == null ? true : globalShortcut;
this.spoofUserAgent = spoofUserAgent == null ? true : spoofUserAgent;
this.userAgent = userAgent == null ? "spoof" : userAgent;
this.validateCertificate = validateCertificate == null ? false : validateCertificate;
this.enableEncoding = enableEncoding == null ? false : enableEncoding;
this.taskList = taskList == null ? true : taskList;
Expand Down Expand Up @@ -57,7 +57,7 @@ class Settings {
data.autoFillClipboard,
data.noPlaylist,
data.globalShortcut,
data.spoofUserAgent,
data.userAgent,
data.validateCertificate,
data.enableEncoding,
data.taskList,
Expand Down Expand Up @@ -94,7 +94,7 @@ class Settings {
this.autoFillClipboard = settings.autoFillClipboard;
this.noPlaylist = settings.noPlaylist;
this.globalShortcut = settings.globalShortcut;
this.spoofUserAgent = settings.spoofUserAgent;
this.userAgent = settings.userAgent;
this.validateCertificate = settings.validateCertificate;
this.enableEncoding = settings.enableEncoding;
this.taskList = settings.taskList;
Expand Down Expand Up @@ -131,7 +131,7 @@ class Settings {
autoFillClipboard: this.autoFillClipboard,
noPlaylist: this.noPlaylist,
globalShortcut: this.globalShortcut,
spoofUserAgent: this.spoofUserAgent,
userAgent: this.userAgent,
validateCertificate: this.validateCertificate,
enableEncoding: this.enableEncoding,
taskList: this.taskList,
Expand All @@ -156,7 +156,6 @@ class Settings {
}

save() {
console.log(this.serialize());
fs.writeFile(this.paths.settings, JSON.stringify(this.serialize()), "utf8").then(() => {
console.log("Saved settings file.")
});
Expand Down
5 changes: 4 additions & 1 deletion modules/types/Query.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ class Query {
args.push("--no-cache-dir");
args.push("--ignore-config");

if(this.environment.settings.spoofUserAgent) {
if(this.environment.settings.userAgent === "spoof") {
args.push("--user-agent"); //Add random user agent to slow down user agent profiling
args.push(new UserAgent({ deviceCategory: 'desktop' }).toString());
} else if(this.environment.settings.userAgent === "empty") {
args.push("--user-agent");
args.push("''"); //Add an empty user agent string to workaround VR video issues
}

if(this.environment.settings.proxy != null && this.environment.settings.proxy.length > 0) {
Expand Down
8 changes: 6 additions & 2 deletions renderer/renderer.html
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,12 @@ <h3>Advanced</h3>
<label class="check-label" for="taskList">Restore the queue after a restart</label>
</div>
<div class="mb-1">
<input class="check-input" type="checkbox" value="" id="spoofUserAgent">
<label class="check-label" for="spoofUserAgent">Spoof user-agent</label>
<label class="check-label" for="userAgent">User Agent</label>
<select class="custom-select d-block rounded w-auto mb-2" id="userAgent">
<option value="spoof" selected>Spoof</option>
<option value="empty">Empty</option>
<option value="default">Default</option>
</select>
</div>
<div class="mb-1">
<input class="check-input" type="checkbox" value="" id="validateCertificate">
Expand Down
4 changes: 2 additions & 2 deletions renderer/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -978,7 +978,7 @@ async function getSettings() {
const settings = await window.main.invoke("settingsAction", {action: "get"});
$('#updateBinary').prop('checked', settings.updateBinary);
$('#updateApplication').prop('checked', settings.updateApplication);
$('#spoofUserAgent').prop('checked', settings.spoofUserAgent);
$('#userAgent').val(settings.userAgent);
$('#validateCertificate').prop('checked', settings.validateCertificate);
$('#enableEncoding').prop('checked', settings.enableEncoding);
$('#taskList').prop('checked', settings.taskList);
Expand Down Expand Up @@ -1014,7 +1014,7 @@ function sendSettings() {
outputFormat: $('#outputFormat').val(),
audioOutputFormat: $('#audioOutputFormat').val(),
proxy: $('#proxySetting').val(),
spoofUserAgent: $('#spoofUserAgent').prop('checked'),
userAgent: $('#userAgent').val(),
validateCertificate: $('#validateCertificate').prop('checked'),
enableEncoding: $('#enableEncoding').prop('checked'),
taskList: $('#taskList').prop('checked'),
Expand Down
42 changes: 26 additions & 16 deletions tests/Query.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,54 +25,64 @@ 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, "python");
const instance = instanceBuilder("spoof", 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");
expect(execa.mock.calls[0][1]).toContain("agent");
});
});
it('adds an empty user agent when this setting is enabled', () => {
UserAgent.prototype.toString = jest.fn().mockReturnValue("agent");
const errorHandlerMock = jest.fn();
const instance = instanceBuilder("empty", null, errorHandlerMock, "python");
return instance.start("https://url.link", [], null).then(() => {
expect(UserAgent.prototype.toString).toBeCalledTimes(0);
expect(execa.mock.calls[0][1]).toContain("--user-agent");
expect(execa.mock.calls[0][1]).toContain("''");
});
});
it('adds the proxy when one is set', () => {
const errorHandlerMock = jest.fn();
const instance = instanceBuilder(false, "a/path/to/cookies.txt", errorHandlerMock, "python", "https://iama.proxy");
const instance = instanceBuilder("default", "a/path/to/cookies.txt", errorHandlerMock, "python", "https://iama.proxy");
return instance.start("https://url.link", [], null).then(() => {
expect(execa.mock.calls[0][1]).toContain("--proxy");
expect(execa.mock.calls[0][1]).toContain("https://iama.proxy");
});
})
it('does not add a proxy when none are set', () => {
const errorHandlerMock = jest.fn();
const instance = instanceBuilder(false, "a/path/to/cookies.txt", errorHandlerMock, "python", "");
const instance = instanceBuilder("default", "a/path/to/cookies.txt", errorHandlerMock, "python", "");
return instance.start("https://url.link", [], null).then(() => {
expect(execa.mock.calls[0][1]).not.toContain("--proxy");
});
})
it('adds the cookies argument when specified in settings', () => {
const errorHandlerMock = jest.fn();
const instance = instanceBuilder(false, "a/path/to/cookies.txt", errorHandlerMock, "python");
const instance = instanceBuilder("default", "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");
const instance = instanceBuilder("default", 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, "python");
const instance = instanceBuilder("default", null, errorHandlerMock, "python");
return instance.start("https://url.link", [], null).then(() => {
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, "python");
const instance = instanceBuilder("default", null, errorHandlerMock, "python");
return instance.start("https://url.link", [], null).then(() => {
expect(execa.mock.calls[0][1]).toContain("--no-cache-dir");
});
Expand All @@ -85,7 +95,7 @@ describe('Query with live callback', () => {
execa.mockReturnValue(mock)
const errorHandlerMock = jest.fn();
const callbackMock = jest.fn();
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
const instance = instanceBuilder("default", null, errorHandlerMock, "python");
const result = instance.start("https://url.link", [], callbackMock);
setTimeout(() => {
instance.stop();
Expand All @@ -99,7 +109,7 @@ describe('Query with live callback', () => {
console.error = jest.fn();
const errorHandlerMock = jest.fn();
const callbackMock = jest.fn();
const instance = instanceBuilder(false, null, errorHandlerMock, "python");
const instance = instanceBuilder("default", null, errorHandlerMock, "python");
const result = instance.start("https://url.link", [], callbackMock);
setTimeout(() => {
stderr.emit("data", "test-error");
Expand All @@ -114,7 +124,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(), "python");
const instance = instanceBuilder("default", null, jest.fn(), "python");
const result = instance.start("https://url.link", [], callbackMock);
setTimeout(() => {
stdout.emit("close");
Expand All @@ -126,7 +136,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(), "python");
const instance = instanceBuilder("default", null, jest.fn(), "python");
const result = instance.start("https://url.link", [], callbackMock);
setTimeout(() => {
stdout.emit("data", "test-data");
Expand All @@ -143,21 +153,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, "python");
const instance = instanceBuilder("default", 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, "python");
const instance = instanceBuilder("default", 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, "python");
const instance = instanceBuilder("default", null, errorHandlerMock, "python");
return instance.start("https://url.link", [], null).then(() => {
expect(errorHandlerMock).toBeCalled();
});
Expand All @@ -171,6 +181,6 @@ function execaMockBuilder(killed) {
return [stdout, stderr, mock];
}

function instanceBuilder(spoofUserAgent, cookiePath, errorHandlerMock, pythonCommand, proxy) {
return new Query({pythonCommand: pythonCommand, errorHandler: {checkError: errorHandlerMock, raiseUnhandledError: errorHandlerMock}, paths: {ytdl: "a/path/to/ytdl"}, settings: {cookiePath: cookiePath, spoofUserAgent: spoofUserAgent, proxy: proxy}}, "test__id");
function instanceBuilder(userAgent, cookiePath, errorHandlerMock, pythonCommand, proxy) {
return new Query({pythonCommand: pythonCommand, errorHandler: {checkError: errorHandlerMock, raiseUnhandledError: errorHandlerMock}, paths: {ytdl: "a/path/to/ytdl"}, settings: {cookiePath: cookiePath, userAgent, proxy: proxy}}, "test__id");
}
4 changes: 2 additions & 2 deletions tests/Settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const fs = require('fs').promises;
const os = require("os");
const Settings = require('../modules/persistence/Settings');
const env = {version: "2.0.0-test1", app: {getPath: jest.fn().mockReturnValue("test/path")}};
const defaultSettingsInstance = new Settings({settings: "tests/test-settings.json"}, env, "none", "none", "test/path", "", "", true, false, true, true, false, false, true, "%(title).200s-(%(height)sp%(fps).0d).%(ext)s", "%(title).200s-(%(height)sp%(fps).0d).%(ext)s", "click", "49", 8, true, "video", true, "C:\\Users\\user\\cookies.txt", false, true, false, false, true, "dark");
const defaultSettings = "{\"outputFormat\":\"none\",\"audioOutputFormat\":\"none\",\"downloadPath\":\"test/path\",\"proxy\":\"\",\"rateLimit\":\"\",\"autoFillClipboard\":true,\"noPlaylist\":false,\"globalShortcut\":true,\"spoofUserAgent\":true,\"validateCertificate\":false,\"enableEncoding\":false,\"taskList\":true,\"nameFormat\":\"%(title).200s-(%(height)sp%(fps).0d).%(ext)s\",\"nameFormatMode\":\"%(title).200s-(%(height)sp%(fps).0d).%(ext)s\",\"sizeMode\":\"click\",\"splitMode\":\"49\",\"maxConcurrent\":8,\"defaultConcurrent\":8,\"updateBinary\":true,\"downloadType\":\"video\",\"updateApplication\":true,\"statSend\":false,\"downloadMetadata\":true,\"downloadThumbnail\":false,\"keepUnmerged\":false,\"calculateTotalSize\":true,\"theme\":\"dark\",\"version\":\"2.0.0-test1\"}"
const defaultSettingsInstance = new Settings({settings: "tests/test-settings.json"}, env, "none", "none", "test/path", "", "", true, false, true, "spoof", false, false, true, "%(title).200s-(%(height)sp%(fps).0d).%(ext)s", "%(title).200s-(%(height)sp%(fps).0d).%(ext)s", "click", "49", 8, true, "video", true, "C:\\Users\\user\\cookies.txt", false, true, false, false, true, "dark");
const defaultSettings = "{\"outputFormat\":\"none\",\"audioOutputFormat\":\"none\",\"downloadPath\":\"test/path\",\"proxy\":\"\",\"rateLimit\":\"\",\"autoFillClipboard\":true,\"noPlaylist\":false,\"globalShortcut\":true,\"userAgent\":\"spoof\",\"validateCertificate\":false,\"enableEncoding\":false,\"taskList\":true,\"nameFormat\":\"%(title).200s-(%(height)sp%(fps).0d).%(ext)s\",\"nameFormatMode\":\"%(title).200s-(%(height)sp%(fps).0d).%(ext)s\",\"sizeMode\":\"click\",\"splitMode\":\"49\",\"maxConcurrent\":8,\"defaultConcurrent\":8,\"updateBinary\":true,\"downloadType\":\"video\",\"updateApplication\":true,\"statSend\":false,\"downloadMetadata\":true,\"downloadThumbnail\":false,\"keepUnmerged\":false,\"calculateTotalSize\":true,\"theme\":\"dark\",\"version\":\"2.0.0-test1\"}"

describe('Load settings from file', () => {
beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion tests/test-settings.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"outputFormat":"none","audioOutputFormat":"none","downloadPath": "test/path","proxy": "","rateLimit": "","autoFillClipboard":true,"noPlaylist": false,"globalShortcut":true,"spoofUserAgent":true,"validateCertificate": false,"enableEncoding": false,"taskList":true,"nameFormat":"%(title).200s-(%(height)sp%(fps).0d).%(ext)s","nameFormatMode":"%(title).200s-(%(height)sp%(fps).0d).%(ext)s","sizeMode":"click","splitMode":"49","maxConcurrent":8,"defaultConcurrent":8,"updateBinary":true,"downloadType":"video","updateApplication":true,"cookiePath":"C:\\Users\\user\\cookies.txt","statSend":false,"downloadMetadata":true,"downloadThumbnail":false,"keepUnmerged":false,"calculateTotalSize":true,"theme": "dark","version":"2.0.0-test1"}
{"outputFormat":"none","audioOutputFormat":"none","downloadPath": "test/path","proxy": "","rateLimit": "","autoFillClipboard":true,"noPlaylist": false,"globalShortcut":true,"userAgent":"spoof","validateCertificate": false,"enableEncoding": false,"taskList":true,"nameFormat":"%(title).200s-(%(height)sp%(fps).0d).%(ext)s","nameFormatMode":"%(title).200s-(%(height)sp%(fps).0d).%(ext)s","sizeMode":"click","splitMode":"49","maxConcurrent":8,"defaultConcurrent":8,"updateBinary":true,"downloadType":"video","updateApplication":true,"cookiePath":"C:\\Users\\user\\cookies.txt","statSend":false,"downloadMetadata":true,"downloadThumbnail":false,"keepUnmerged":false,"calculateTotalSize":true,"theme": "dark","version":"2.0.0-test1"}

0 comments on commit 84e82b7

Please sign in to comment.