Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature : replace original filename when saving #1379

Merged
merged 1 commit into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,18 @@
"message": "caption",
"description": "[options] Add download caption option"
},
"optDownloadReplaceOriginalFilenameTooltip": {
"message": "Replace original filename or remove it if no value supplied (extension is preserved)",
"description": "[options] Tooltip for replace original filename when downloading option"
},
"optDownloadReplaceOriginalFilenamePlaceholder": {
"message": "Example: fullsize_image",
"description": "[options] Placeholder for download folder option"
},
"optDownloadReplaceOriginalFilename": {
"message": "On save, replace original filename by:",
"description": "[options] Replace original filename when downloading option"
},
"optSectionTroubleshooting": {
"message": "Troubleshooting",
"description": "[options] Page section for troubleshooting"
Expand Down
9 changes: 9 additions & 0 deletions html/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,15 @@ <h1 class="ten columns text-center" data-i18n="optTitle"></h1>
</div>
</div>
</div>
<div class="ttip" data-i18n-tooltip="optDownloadReplaceOriginalFilenameTooltip">
<li class="append field">
<label class="checkbox inline" style="padding-right:10px" for="chkDownloadReplaceOriginalFilename">
<input type="checkbox" id="chkDownloadReplaceOriginalFilename"><span></span>
<div style="display:inline" data-i18n="optDownloadReplaceOriginalFilename"></div>
</label>
<input class="normal text input" type="text" id="txtDownloadReplaceOriginalFilename" data-i18n-placeholder="optDownloadReplaceOriginalFilenamePlaceholder">
</li>
</div>
</ul>
</fieldset>
<fieldset>
Expand Down
4 changes: 4 additions & 0 deletions js/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ var factorySettings = {
addDownloadDuration : false,
addDownloadIndex : false,
addDownloadCaption : false,
replaceOriginalFilename : false,
downloadFilename : '',
useSeparateTabOrWindowForUnloadableUrlsEnabled: false,
useSeparateTabOrWindowForUnloadableUrls: 'window',
captionLocation : 'below',
Expand Down Expand Up @@ -139,6 +141,8 @@ function loadOptions() {
options.addDownloadDuration = options.hasOwnProperty('addDownloadDuration') ? options.addDownloadDuration : factorySettings.addDownloadDuration;
options.addDownloadIndex = options.hasOwnProperty('addDownloadIndex') ? options.addDownloadIndex : factorySettings.addDownloadIndex;
options.addDownloadCaption = options.hasOwnProperty('addDownloadCaption') ? options.addDownloadCaption : factorySettings.addDownloadCaption;
options.replaceOriginalFilename = options.hasOwnProperty('replaceOriginalFilename') ? options.replaceOriginalFilename : factorySettings.replaceOriginalFilename;
options.downloadFilename = options.hasOwnProperty('downloadFilename') ? options.downloadFilename : factorySettings.downloadFilename;
options.useSeparateTabOrWindowForUnloadableUrlsEnabled = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrlsEnabled') ? options.useSeparateTabOrWindowForUnloadableUrlsEnabled : factorySettings.useSeparateTabOrWindowForUnloadableUrlsEnabled;
options.useSeparateTabOrWindowForUnloadableUrls = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrls') ? options.useSeparateTabOrWindowForUnloadableUrls : factorySettings.useSeparateTabOrWindowForUnloadableUrls;

Expand Down
132 changes: 81 additions & 51 deletions js/hoverzoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -1940,7 +1940,7 @@ var hoverZoom = {
// if video comes with distinct url for audio then extension = video's extension
details.extension = getExtensionFromUrl(srcDetails.audio && !srcDetails.video ? srcDetails.audioUrl : srcDetails.url, srcDetails.video, srcDetails.playlist, srcDetails.audio);
details.host = srcDetails.host;
let filename = getDownloadFilename();
let filename = getFilename();
if (filename) details.filename = filename;
let duration = (srcDetails.audio && !srcDetails.video ? getDurationFromAudio() : getDurationFromVideo());
if (duration) details.duration = duration.replace(/ /g, ':');
Expand Down Expand Up @@ -3040,7 +3040,7 @@ var hoverZoom = {
function downloadResource(url, filename, callback) {
cLog('download: ' + url);
if (!filename) filename = url.split('\\').pop().split('/').pop();

if (filename.startsWith('.')) filename = 'download' + filename;
// prefix with download folder if needed
if (options.downloadFolder) {
cLog('options.downloadFolder: ' + options.downloadFolder);
Expand Down Expand Up @@ -3073,98 +3073,127 @@ var hoverZoom = {
}
}

// 4 types of media can be saved to disk: image, video, audio, playlist
const downloadMedias = {
// 5 types of media can be saved to disk: image, video, audio, playlist, subtitles
const fileMedias = {
IMG : "IMG",
VIDEO : "VIDEO",
AUDIO : "AUDIO",
PLAYLIST : "PLAYLIST",
SUBTITLES : "SUBTITLES"
}

// return download filename without knowing type of download
function getDownloadFilename() {
// return filename without knowing type of media displayed
function getFilename() {

let filename = getDownloadFilenameByMedia(downloadMedias.IMG);
let filename = getFilenameByMedia(fileMedias.IMG, false);
if (filename) return filename;
filename = getDownloadFilenameByMedia(downloadMedias.VIDEO);
filename = getFilenameByMedia(fileMedias.VIDEO, false);
if (filename) return filename;
filename = getDownloadFilenameByMedia(downloadMedias.AUDIO);
filename = getFilenameByMedia(fileMedias.AUDIO, false);
if (filename) return filename;
filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST);
filename = getFilenameByMedia(fileMedias.PLAYLIST, false);
if (filename) return filename;
filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES);
filename = getFilenameByMedia(fileMedias.SUBTITLES, false);
if (filename) return filename;
return '';
}

// return download filename according to type of download in param
function getDownloadFilenameByMedia(downloadMedia) {
function replaceOriginalFilename(filename) {
if (options.replaceOriginalFilename) {
if (filename.indexOf('.') !== -1) filename = filename.replace(/(.*)\.(.*)/, `${options.downloadFilename}.$2`);
else filename = options.downloadFilename;
}
return filename;
}

// return original or download filename according to type of media in param
function getFilenameByMedia(fileMedia, download = true) {

let src, filename;
switch (downloadMedia) {
case downloadMedias.IMG:
if (!hz.hzViewer) return '';
switch (fileMedia) {
case fileMedias.IMG:
if (!hz.hzViewer) return undefined;
let img = hz.hzViewer.find('img:not(.hzPlaceholder)').get(0);
if (!img) return '';
if (!img) return undefined;
src = img.src;
// remove trailing / & trailing query
src = src.replace(/\/$/, '').split(/[\?!#&]/)[0];
// extract filename
filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, '');
if (filename == '') filename = 'image';
if (filename.indexOf('.') === -1) filename = filename + '.jpg';
if (filename === '') {
filename = 'image';
}
if (download) {
filename = replaceOriginalFilename(filename);
if (filename.indexOf('.') === -1) filename = filename + '.jpg'; // add default extension for download
}
return filename;

case downloadMedias.VIDEO:
if (!hz.hzViewer) return '';
case fileMedias.VIDEO:
if (!hz.hzViewer) return undefined;
let video = hz.hzViewer.find('video').get(0);
if (!video) return '';
if (!video) return undefined;
src = video.src;
if (src.startsWith('blob:')) return '';
if (src.startsWith('blob:')) return undefined;
// remove trailing / & trailing query
src = src.replace(/\/$/, '').split(/[\?!#&]/)[0];
// extract filename
filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, '');
if (filename == '') filename = 'video';
if (filename.indexOf('.') === -1) filename = filename + '.mp4';
if (filename === '') {
filename = 'video';
}
if (download) {
filename = replaceOriginalFilename(filename);
if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download
}
return filename;

case downloadMedias.AUDIO:
if (!hz.hzViewer) return '';
case fileMedias.AUDIO:
if (!hz.hzViewer) return undefined;
let audio = hz.hzViewer.find('audio').get(0);
if (!audio) return '';
if (!audio) return undefined;
src = audio.src;
// remove trailing / & trailing query
src = src.replace(/\/$/, '').split(/[\?!#&]/)[0];
// extract filename
filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, '');
if (filename == '') filename = 'audio';
if (filename.indexOf('.') === -1) filename = filename + '.mp4';
if (filename === '') {
filename = 'audio';
}
if (download) {
filename = replaceOriginalFilename(filename);
if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download
}
return filename;

case downloadMedias.PLAYLIST:
if (!hz.hzViewer) return '';
if (!srcDetails.playlist) return '';
case fileMedias.PLAYLIST:
if (!hz.hzViewer) return undefined;
if (!srcDetails.playlist) return undefined;
src = srcDetails.url;
// remove trailing / & trailing query
src = src.replace(/\/$/, '').split(/[\?!#&]/)[0];
// extract filename
filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, '');
filename = 'playlist-' + filename;
if (filename.indexOf('.') === -1) filename = filename + '.m3u8';
if (download) {
filename = replaceOriginalFilename(filename);
filename = 'playlist-' + filename;
if (filename.indexOf('.') === -1) filename = filename + '.m3u8'; // add default extension for download
}
return filename;

case downloadMedias.SUBTITLES:
if (!hz.hzViewer) return '';
if (!srcDetails.subtitlesUrl) return '';
case fileMedias.SUBTITLES:
if (!hz.hzViewer) return undefined;
if (!srcDetails.subtitlesUrl) return undefined;
src = srcDetails.subtitlesUrl;
// remove trailing / & trailing query
src = src.replace(/\/$/, '').split(/[\?!#&]/)[0];
// extract filename
filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, '');
filename = 'subtitles-' + filename;
if (filename.indexOf('.') === -1) filename = filename + '.txt';
if (download) {
filename = replaceOriginalFilename(filename);
filename = 'subtitles-' + filename;
if (filename.indexOf('.') === -1) filename = filename + '.txt'; // add default extension for download
}
return filename;
}
return '';
Expand Down Expand Up @@ -3242,7 +3271,7 @@ var hoverZoom = {
let img = hz.hzViewer.find('img').get(0);
if (!img) return;
let src = img.src;
let filename = getDownloadFilenameByMedia(downloadMedias.IMG);
let filename = getFilenameByMedia(fileMedias.IMG);
if (!filename) return;

if (options.addDownloadCaption) {
Expand Down Expand Up @@ -3283,7 +3312,7 @@ var hoverZoom = {
if (!video) return;
let src = video.src;
if (src.startsWith('blob:')) return;
let filename = getDownloadFilenameByMedia(downloadMedias.VIDEO);
let filename = getFilenameByMedia(fileMedias.VIDEO);
if (!filename) return;

if (options.addDownloadCaption) {
Expand Down Expand Up @@ -3317,7 +3346,7 @@ var hoverZoom = {
let audio = hz.hzViewer.find('audio').get(0);
if (!audio) return;
let src = audio.src;
let filename = getDownloadFilenameByMedia(downloadMedias.AUDIO);
let filename = getFilenameByMedia(fileMedias.AUDIO);
if (!filename) return;

if (options.addDownloadCaption) {
Expand Down Expand Up @@ -3346,7 +3375,7 @@ var hoverZoom = {
let video = hz.hzViewer.find('video').get(0);
let audio = hz.hzViewer.find('audio').get(0);
if (!video && !audio) return;
let filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES);
let filename = getFilenameByMedia(fileMedias.SUBTITLES);
if (!filename) return;

if (options.addDownloadCaption) {
Expand All @@ -3371,7 +3400,7 @@ var hoverZoom = {
if (!hz.hzViewer) return;
let video = hz.hzViewer.find('video').get(0);
if (!video) return;
let filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST);
let filename = getFilenameByMedia(fileMedias.PLAYLIST);
if (!filename) return;

if (options.addDownloadCaption) {
Expand All @@ -3398,12 +3427,6 @@ var hoverZoom = {
filename = origin + filename;
}

// prefix with download folder if needed
if (options.downloadFolder) {
let downloadFolder = options.downloadFolder;
filename = downloadFolder + filename;
}

// download KO: This function must be called during a user gesture => debugger must be closed
downloadResource(srcDetails.url, filename);
savePlaylistAsMP3MP4(filename);
Expand All @@ -3412,6 +3435,13 @@ var hoverZoom = {
// - filename.m3u8.mp4 (video part)
// - filename.m3u8.mp3 (audio part)
function savePlaylistAsMP3MP4(filename) {
// prefix with download folder if needed
if (options.downloadFolder) {
console.log('options.downloadFolder: ' + options.downloadFolder);
let downloadFolder = options.downloadFolder;
filename = downloadFolder + filename;
console.log('filename: ' + filename);
}
// audio
if (fmp4Data['audio'].length) {
const blobAudio = new Blob([arrayConcat(fmp4Data['audio'])], {type:'application/octet-stream'});
Expand Down
24 changes: 24 additions & 0 deletions js/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ function saveOptions() {
options.addDownloadDuration = $('#chkAddDownloadDuration')[0].checked;
options.addDownloadIndex = $('#chkAddDownloadIndex')[0].checked;
options.addDownloadCaption = $('#chkAddDownloadCaption')[0].checked;
options.replaceOriginalFilename = $('#chkDownloadReplaceOriginalFilename')[0].checked;
options.downloadFilename = $('#txtDownloadReplaceOriginalFilename')[0].value;
options.debug = $('#chkEnableDebug')[0].checked;
options.useSeparateTabOrWindowForUnloadableUrlsEnabled = $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked;
options.useSeparateTabOrWindowForUnloadableUrls = $('#selectUseSeparateTabOrWindowForUnloadableUrls').val();
Expand Down Expand Up @@ -259,6 +261,8 @@ function restoreOptions(optionsFromFactorySettings) {
$('#chkAddDownloadDuration').trigger(options.addDownloadDuration ? 'gumby.check' : 'gumby.uncheck');
$('#chkAddDownloadIndex').trigger(options.addDownloadIndex ? 'gumby.check' : 'gumby.uncheck');
$('#chkAddDownloadCaption').trigger(options.addDownloadCaption ? 'gumby.check' : 'gumby.uncheck');
$('#chkDownloadReplaceOriginalFilename').trigger(options.replaceOriginalFilename ? 'gumby.check' : 'gumby.uncheck');
$('#txtDownloadReplaceOriginalFilename').val(options.downloadFilename);
$('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').trigger(options.useSeparateTabOrWindowForUnloadableUrlsEnabled ? 'gumby.check' : 'gumby.uncheck');
$('#selectUseSeparateTabOrWindowForUnloadableUrls').val(options.useSeparateTabOrWindowForUnloadableUrls);
$('#chkEnableDebug').trigger(options.debug ? 'gumby.check' : 'gumby.uncheck');
Expand Down Expand Up @@ -453,6 +457,16 @@ function downloadFolderOnChange(val) {
return this.value;
}

// validate user input
function replaceOriginalFilenameOnChange(val) {
let value = (typeof val == 'string' ? val : this.value);
value = value.trim();
// remove Windows Explorer forbidden characters for file name -> : * ? " < > | /
value = value.replace(/[!*:?"<>|\/\\]/g, '');
this.value = value;
return this.value;
}

function updateDivAmbilight() {
if ($('#chkAmbilightEnabled')[0].checked) {
$('#divAmbilight').removeClass('disabled');
Expand All @@ -461,6 +475,14 @@ function updateDivAmbilight() {
}
}

function updateDownloadReplaceOriginalFilename() {
if ($('#chkDownloadReplaceOriginalFilename')[0].checked) {
$('#txtDownloadReplaceOriginalFilename').removeClass('disabled');
} else {
$('#txtDownloadReplaceOriginalFilename').addClass('disabled');
}
}

function updateUseSeparateTabOrWindowForUnloadableUrls() {
if ($('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked) {
$('#selectUseSeparateTabOrWindowForUnloadableUrls').removeClass('disabled');
Expand Down Expand Up @@ -658,6 +680,8 @@ $(function () {
$('#btnAddExcludedSite').click(btnAddExcludedSiteOnClick);
$('#btnRemoveExcludedSite').click(btnRemoveExcludedSiteOnClick);
$('#txtDownloadFolder').change(downloadFolderOnChange);
$('#chkDownloadReplaceOriginalFilename').parent().on('gumby.onChange', updateDownloadReplaceOriginalFilename);
$('#txtDownloadReplaceOriginalFilename').change(replaceOriginalFilenameOnChange);
$('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').parent().on('gumby.onChange', updateUseSeparateTabOrWindowForUnloadableUrls);
$('#chkHideMouseCursor').parent().on('gumby.onChange', updateDivHideMouseCursor);
$('#chkDarkMode').parent().on('gumby.onChange', updateDarkMode);
Expand Down