Skip to content

Commit

Permalink
Merge pull request #1379 from GrosPoulet/master
Browse files Browse the repository at this point in the history
New feature : replace original filename when saving
  • Loading branch information
GrosPoulet authored Jul 18, 2024
2 parents 031ba26 + 18dac7e commit 3aa4bed
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 51 deletions.
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

0 comments on commit 3aa4bed

Please sign in to comment.