feat(download): show notice download url leads to another website
Closes #161
This commit is contained in:
parent
e85b33d95d
commit
3d7671c303
@ -11,7 +11,7 @@ import {
|
||||
import { toast } from 'react-toastify'
|
||||
import { BlogCard } from '../../components/BlogCard'
|
||||
import { ProfileSection } from '../../components/ProfileSection'
|
||||
import { useAppSelector, useBodyScrollDisable } from '../../hooks'
|
||||
import { useAppSelector, useBodyScrollDisable, useDidMount } from '../../hooks'
|
||||
import { getGamePageRoute, getModsEditPageRoute } from '../../routes'
|
||||
import '../../styles/comments.css'
|
||||
import '../../styles/downloads.css'
|
||||
@ -26,6 +26,7 @@ import '../../styles/write.css'
|
||||
import { DownloadUrl, ModPageLoaderResult } from '../../types'
|
||||
import {
|
||||
capitalizeEachWord,
|
||||
checkUrlForFile,
|
||||
copyTextToClipboard,
|
||||
downloadFile,
|
||||
getFilenameFromUrl
|
||||
@ -613,6 +614,12 @@ const Download = ({
|
||||
customNote
|
||||
}: DownloadUrl) => {
|
||||
const [showAuthDetails, setShowAuthDetails] = useState(false)
|
||||
const [showNotice, setShowNotice] = useState(false)
|
||||
|
||||
useDidMount(async () => {
|
||||
const isFile = await checkUrlForFile(url)
|
||||
setShowNotice(!isFile)
|
||||
})
|
||||
|
||||
const handleDownload = () => {
|
||||
// Get the filename from the URL
|
||||
@ -632,6 +639,16 @@ const Download = ({
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
{showNotice && (
|
||||
<div className='IBMSMSMBSSNote'>
|
||||
<p>
|
||||
Notice: The creator has provided a download link that doesn't
|
||||
download the files immediately, but rather redirects you to a
|
||||
different site.
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{/*temporarily commented out the WoT rating for download links within a mod post
|
||||
<div className='IBMSMSMBSSDownloadsElementInside'>
|
||||
<p>Ratings (WIP):</p>
|
||||
|
102
src/utils/url.ts
102
src/utils/url.ts
@ -119,3 +119,105 @@ export const downloadFile = (url: string, filename: string) => {
|
||||
// Remove the anchor from the document
|
||||
document.body.removeChild(a)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the url endpoint returns a file
|
||||
* @param url
|
||||
* @returns true if matches a possible file download
|
||||
*/
|
||||
export const checkUrlForFile = async (url: string) => {
|
||||
try {
|
||||
// HTTP HEAD request to get headers without downloading the full content
|
||||
const response = await fetch(url, { method: 'HEAD' })
|
||||
|
||||
// Check Content-Disposition header
|
||||
const contentDisposition = response.headers.get('content-disposition')
|
||||
if (contentDisposition && contentDisposition.includes('attachment')) {
|
||||
return true
|
||||
}
|
||||
|
||||
//Check Content-Type header
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (
|
||||
contentType &&
|
||||
(contentType.includes('application/') ||
|
||||
contentType.includes('image/') ||
|
||||
contentType.includes('audio/') ||
|
||||
contentType.includes('video/'))
|
||||
) {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
// Check if blossom file (link includes sha256 string in the url)
|
||||
// Most likely it's a file that users would directly download
|
||||
const regex = /\/[a-fA-F0-9]{64}(\.[a-zA-Z0-9]+)?$/
|
||||
if (regex.test(url)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Common mod file extensions
|
||||
const fileExtensions = [
|
||||
'.zip',
|
||||
'.rar',
|
||||
'.7z',
|
||||
'.tar',
|
||||
'.gz',
|
||||
'.bz2',
|
||||
'.xz',
|
||||
'.cab',
|
||||
'.iso', // (can also be considered a disk image)
|
||||
'.tgz', // (tar.gz)
|
||||
'.z', // (compress)
|
||||
'.lz', // (Lempel-Ziv)
|
||||
'.mp3',
|
||||
'.wav',
|
||||
'.aac',
|
||||
'.flac',
|
||||
'.ogg',
|
||||
'.wma',
|
||||
'.m4a',
|
||||
'.opus',
|
||||
'.mp4',
|
||||
'.avi',
|
||||
'.mkv',
|
||||
'.mov',
|
||||
'.wmv',
|
||||
'.flv',
|
||||
'.webm',
|
||||
'.mpeg',
|
||||
'.3gp',
|
||||
'.jpg',
|
||||
'.jpeg',
|
||||
'.png',
|
||||
'.gif',
|
||||
'.bmp',
|
||||
'.tiff',
|
||||
'.tif',
|
||||
'.svg',
|
||||
'.raw',
|
||||
'.heic',
|
||||
'.exe', // (executable files for Windows games)
|
||||
'.apk', // (Android Package)
|
||||
'.ipa', // (iOS App Store Package)
|
||||
'.bin', // (binary file, often used for game data)
|
||||
'.iso', // (disk image, often for console games)
|
||||
'.sav', // (save game file)
|
||||
'.cfg', // (configuration file)
|
||||
'.mod', // (modification file)
|
||||
'.pak', // (package file, often used in games)
|
||||
'.unity', // (Unity game project file)
|
||||
'.game', // (generic game file)
|
||||
'.dmg' // (disk image for macOS applications)
|
||||
]
|
||||
|
||||
for (const ext of fileExtensions) {
|
||||
if (url.endsWith(ext)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user