diff --git a/src/components/Dialogs/BulkRenameFilesDialog.vue b/src/components/Dialogs/BulkRenameFilesDialog.vue
index 8cdc2268..c7a89de7 100644
--- a/src/components/Dialogs/BulkRenameFilesDialog.vue
+++ b/src/components/Dialogs/BulkRenameFilesDialog.vue
@@ -1,7 +1,8 @@
 <script setup lang="ts">
 import HistoryField from '@/components/Core/HistoryField.vue'
 import { useDialog } from '@/composables'
-import { getFileIcon, HistoryKey } from '@/constants/vuetorrent'
+import { HistoryKey } from '@/constants/vuetorrent'
+import { getFileIcon } from '@/helpers'
 import { useContentStore } from '@/stores'
 import { TreeFolder, TreeNode } from '@/types/vuetorrent'
 import { computed, onMounted, reactive, readonly, ref, watch } from 'vue'
diff --git a/src/components/Dialogs/ContentFilterDialog.vue b/src/components/Dialogs/ContentFilterDialog.vue
index 565422a8..ec11b3c3 100644
--- a/src/components/Dialogs/ContentFilterDialog.vue
+++ b/src/components/Dialogs/ContentFilterDialog.vue
@@ -1,11 +1,10 @@
 <script setup lang="ts">
-import { useDialog } from '@/composables'
+import { useDialog, useI18nUtils } from '@/composables'
 import { FilePriority } from '@/constants/qbit'
-import { comparators, formatData } from '@/helpers'
-import { splitExt } from '@/helpers/path.ts'
+import { FileType } from '@/constants/vuetorrent'
+import { comparators, formatData, getExtType, splitExt } from '@/helpers'
 import { useContentStore, useVueTorrentStore } from '@/stores'
 import { computed, reactive } from 'vue'
-import { useI18nUtils } from '@/composables'
 
 const props = defineProps<{
   guid: string
@@ -21,15 +20,35 @@ const sizeBoundaries = computed<[number, number]>(() =>
     .map(file => file.size)
     .reduce((prev, curr) => [prev[0] === -1 || curr < prev[0] ? curr : prev[0], prev[1] === -1 || curr > prev[1] ? curr : prev[1]], [-1, -1])
 )
-const fileExtensions = computed(() => Array.from(new Set<string>(contentStore.cachedFiles.map(file => splitExt(file.name)[1])).values()))
-const extensionItems = computed(() =>
-  fileExtensions.value
-    .map(ext => {
-      if (ext === '') return { title: t('common.none'), value: '' }
-      else return { title: `.${ext}`, value: ext }
-    })
-    .sort((a, b) => comparators.text.asc(a.title, b.title))
+
+const fileExtensionsByType = computed(() =>
+  new Set<string>(contentStore.cachedFiles.map(file => splitExt(file.name)[1])).values().reduce(
+    (prev, ext) => {
+      const type = getExtType(ext)
+      if (Object.keys(prev).includes(type)) {
+        prev[type].push(ext)
+      } else {
+        prev[type] = [ext]
+      }
+      return prev
+    },
+    {} as Record<FileType, string[]>
+  )
 )
+const extensionItems = computed(() =>
+  Object.entries(fileExtensionsByType.value)
+    .sort(([typeA, _1], [typeB, _2]) => {
+      if (typeA === FileType.UNKNOWN) return 1
+      if (typeB === FileType.UNKNOWN) return -1
+      return comparators.text.asc(typeA, typeB)
+    })
+    .flatMap(([type, extensions]) => [
+      { props: { header: t(`constants.file_type.${type}`) } },
+      ...extensions.map(ext => ({ title: `.${ext}`, value: ext })),
+      { props: { divider: true } }
+    ])
+)
+
 const priorityOptions = [
   {
     title: t('constants.file_priority.unwanted'),
@@ -91,30 +110,38 @@ function close() {
     <v-card>
       <v-card-title class="ios-margin">
         <v-toolbar color="transparent">
-          <v-toolbar-title>{{ $t('torrentDetail.content.filter.title') }}</v-toolbar-title>
+          <v-toolbar-title>{{ t('torrentDetail.content.filter.title') }}</v-toolbar-title>
           <v-btn icon="mdi-close" @click="close" />
         </v-toolbar>
       </v-card-title>
       <v-card-text>
         <v-row>
           <v-col cols="4" class="d-flex align-center">
-            {{ $t('torrentDetail.content.filter.extensions') }}
+            {{ t('torrentDetail.content.filter.extensions') }}
           </v-col>
           <v-col cols="8">
-            <v-select v-model="filters.extensions" :items="extensionItems" :placeholder="$t('common.disabled')" persistent-placeholder multiple hide-details />
+            <v-select v-model="filters.extensions" :items="extensionItems" :placeholder="t('common.disabled')" persistent-placeholder multiple hide-details>
+              <template #item="data">
+                <v-list-subheader v-if="data.props.header">
+                  {{ data.props.header }}
+                </v-list-subheader>
+                <v-divider v-else-if="data.props.divider" />
+                <v-list-item v-else v-bind="data.props" />
+              </template>
+            </v-select>
           </v-col>
         </v-row>
         <v-row>
           <v-col cols="4" class="d-flex align-center">
-            {{ $t('torrentDetail.content.filter.priority') }}
+            {{ t('torrentDetail.content.filter.priority') }}
           </v-col>
           <v-col cols="8">
-            <v-select v-model="filters.priority" :items="priorityOptions" :placeholder="$t('common.disabled')" persistent-placeholder multiple hide-details />
+            <v-select v-model="filters.priority" :items="priorityOptions" :placeholder="t('common.disabled')" persistent-placeholder multiple hide-details />
           </v-col>
         </v-row>
         <v-row>
           <v-col cols="4" class="d-flex align-center">
-            {{ $t('torrentDetail.content.filter.size') }}
+            {{ t('torrentDetail.content.filter.size') }}
           </v-col>
           <v-col cols="8">
             <v-range-slider
@@ -145,7 +172,7 @@ function close() {
         <v-row>
           <v-col cols="12">
             {{
-              $t('torrentDetail.content.filter.preview', {
+              t('torrentDetail.content.filter.preview', {
                 count: filterPreview.length,
                 total: contentStore.cachedFiles.length,
                 size: formatData(filterPreviewSize, vuetorrentStore.useBinarySize)
@@ -155,8 +182,8 @@ function close() {
         </v-row>
       </v-card-text>
       <v-card-actions>
-        <v-btn color="error" @click="exclude">{{ $t('torrentDetail.content.filter.exclude') }}</v-btn>
-        <v-btn color="success" @click="include">{{ $t('torrentDetail.content.filter.include') }}</v-btn>
+        <v-btn color="error" @click="exclude">{{ t('torrentDetail.content.filter.exclude') }}</v-btn>
+        <v-btn color="success" @click="include">{{ t('torrentDetail.content.filter.include') }}</v-btn>
       </v-card-actions>
     </v-card>
   </v-dialog>
diff --git a/src/components/TorrentDetail/Content/ContentNode.vue b/src/components/TorrentDetail/Content/ContentNode.vue
index 373dfd12..8ebe0478 100644
--- a/src/components/TorrentDetail/Content/ContentNode.vue
+++ b/src/components/TorrentDetail/Content/ContentNode.vue
@@ -1,8 +1,7 @@
 <script setup lang="ts">
 import { useI18nUtils } from '@/composables'
 import { FilePriority } from '@/constants/qbit'
-import { getFileIcon } from '@/constants/vuetorrent'
-import { doesCommand, formatData } from '@/helpers'
+import { doesCommand, formatData, getFileIcon } from '@/helpers'
 import { useContentStore, useVueTorrentStore } from '@/stores'
 import { TreeNode } from '@/types/vuetorrent'
 import { storeToRefs } from 'pinia'
diff --git a/src/constants/vuetorrent/FileIcon.ts b/src/constants/vuetorrent/FileIcon.ts
index 442a19c4..ab7e7386 100644
--- a/src/constants/vuetorrent/FileIcon.ts
+++ b/src/constants/vuetorrent/FileIcon.ts
@@ -1,57 +1,122 @@
-enum FileIcon {
-  PDF = 'mdi-file-pdf-box',
-  IMAGE = 'mdi-file-image',
-  DOCUMENT = 'mdi-file-document',
-  INFORMATION = 'mdi-information-variant-box',
-  MUSIC = 'mdi-music',
-  VIDEO = 'mdi-movie',
-  SUBTITLE = 'mdi-subtitles',
-  ARCHIVE = 'mdi-zip-box-outline',
-  EXECUTABLE = 'mdi-application-brackets'
+export enum FileType {
+  ARCHIVE = 'archive',
+  AUDIO = 'audio',
+  BOOK = 'book',
+  DOCUMENT = 'document',
+  EXECUTABLE = 'executable',
+  IMAGE = 'image',
+  INFORMATION = 'information',
+  SCRIPT = 'script',
+  SUBTITLE = 'subtitle',
+  VIDEO = 'video',
+
+  UNKNOWN = 'unknown'
 }
 
-export const typesMap: Record<string, FileIcon> = {
-  pdf: FileIcon.PDF,
+export const typesMap: Record<FileType, string> = {
+  [FileType.ARCHIVE]: 'mdi-zip-box-outline',
+  [FileType.AUDIO]: 'mdi-audio',
+  [FileType.BOOK]: 'mdi-book-open-blank-variant',
+  [FileType.DOCUMENT]: 'mdi-file-document',
+  [FileType.EXECUTABLE]: 'mdi-application-brackets',
+  [FileType.IMAGE]: 'mdi-file-image',
+  [FileType.INFORMATION]: 'mdi-information-variant-box',
+  [FileType.SCRIPT]: 'mdi-script-text',
+  [FileType.SUBTITLE]: 'mdi-subtitles',
+  [FileType.VIDEO]: 'mdi-movie',
 
-  png: FileIcon.IMAGE,
-  jpg: FileIcon.IMAGE,
-  jpeg: FileIcon.IMAGE,
-  tiff: FileIcon.IMAGE,
-
-  doc: FileIcon.DOCUMENT,
-  docx: FileIcon.DOCUMENT,
-  txt: FileIcon.DOCUMENT,
-
-  nfo: FileIcon.INFORMATION,
-
-  mp3: FileIcon.MUSIC,
-  wav: FileIcon.MUSIC,
-  flac: FileIcon.MUSIC,
-
-  avi: FileIcon.VIDEO,
-  mp4: FileIcon.VIDEO,
-  mkv: FileIcon.VIDEO,
-  mov: FileIcon.VIDEO,
-  wmv: FileIcon.VIDEO,
-
-  srt: FileIcon.SUBTITLE,
-  idx: FileIcon.SUBTITLE,
-  sub: FileIcon.SUBTITLE,
-
-  rar: FileIcon.ARCHIVE,
-  zip: FileIcon.ARCHIVE,
-  gz: FileIcon.ARCHIVE,
-  '7z': FileIcon.ARCHIVE,
-  iso: FileIcon.ARCHIVE,
-
-  exe: FileIcon.EXECUTABLE,
-  msi: FileIcon.EXECUTABLE,
-  dmg: FileIcon.EXECUTABLE,
-  deb: FileIcon.EXECUTABLE,
-  jar: FileIcon.EXECUTABLE
+  [FileType.UNKNOWN]: 'mdi-file'
 }
 
-export function getFileIcon(filename: string) {
-  const type = filename.split('.').pop()?.toLowerCase() || ''
-  return typesMap[type] || 'mdi-file'
+export const extMap: Record<string, FileType> = {
+  '7z': FileType.ARCHIVE,
+  bz2: FileType.ARCHIVE,
+  cab: FileType.ARCHIVE,
+  gz: FileType.ARCHIVE,
+  iso: FileType.ARCHIVE,
+  rar: FileType.ARCHIVE,
+  sfx: FileType.ARCHIVE,
+  tar: FileType.ARCHIVE,
+  tgz: FileType.ARCHIVE,
+  xz: FileType.ARCHIVE,
+  zip: FileType.ARCHIVE,
+
+  alac: FileType.AUDIO,
+  flac: FileType.AUDIO,
+  mp3: FileType.AUDIO,
+  ogg: FileType.AUDIO,
+  wav: FileType.AUDIO,
+  wma: FileType.AUDIO,
+
+  cb7: FileType.BOOK,
+  cbr: FileType.BOOK,
+  cbt: FileType.BOOK,
+  cbz: FileType.BOOK,
+  epub: FileType.BOOK,
+  mobi: FileType.BOOK,
+
+  doc: FileType.DOCUMENT,
+  docx: FileType.DOCUMENT,
+  htm: FileType.DOCUMENT,
+  html: FileType.DOCUMENT,
+  pdf: FileType.DOCUMENT,
+  rtf: FileType.DOCUMENT,
+  txt: FileType.DOCUMENT,
+  xhtml: FileType.DOCUMENT,
+
+  apk: FileType.EXECUTABLE,
+  app: FileType.EXECUTABLE,
+  bin: FileType.EXECUTABLE,
+  deb: FileType.EXECUTABLE,
+  dmg: FileType.EXECUTABLE,
+  exe: FileType.EXECUTABLE,
+  jar: FileType.EXECUTABLE,
+  msi: FileType.EXECUTABLE,
+
+  avif: FileType.IMAGE,
+  bmp: FileType.IMAGE,
+  gif: FileType.IMAGE,
+  heif: FileType.IMAGE,
+  jfif: FileType.IMAGE,
+  jpeg: FileType.IMAGE,
+  jpg: FileType.IMAGE,
+  png: FileType.IMAGE,
+  svg: FileType.IMAGE,
+  tiff: FileType.IMAGE,
+  webp: FileType.IMAGE,
+
+  nfo: FileType.INFORMATION,
+
+  bat: FileType.SCRIPT,
+  c: FileType.SCRIPT,
+  cmd: FileType.SCRIPT,
+  com: FileType.SCRIPT,
+  cpp: FileType.SCRIPT,
+  cs: FileType.SCRIPT,
+  css: FileType.SCRIPT,
+  h: FileType.SCRIPT,
+  hpp: FileType.SCRIPT,
+  java: FileType.SCRIPT,
+  js: FileType.SCRIPT,
+  py: FileType.SCRIPT,
+  vbs: FileType.SCRIPT,
+
+  idx: FileType.SUBTITLE,
+  srt: FileType.SUBTITLE,
+  sub: FileType.SUBTITLE,
+
+  '3gp': FileType.VIDEO,
+  avi: FileType.VIDEO,
+  flv: FileType.VIDEO,
+  gifv: FileType.VIDEO,
+  m2ts: FileType.VIDEO,
+  m4v: FileType.VIDEO,
+  mkv: FileType.VIDEO,
+  mov: FileType.VIDEO,
+  mp4: FileType.VIDEO,
+  mpeg: FileType.VIDEO,
+  mpg: FileType.VIDEO,
+  mts: FileType.VIDEO,
+  ts: FileType.VIDEO,
+  wmv: FileType.VIDEO,
 }
diff --git a/src/constants/vuetorrent/index.ts b/src/constants/vuetorrent/index.ts
index 0b9ee34d..ff9e893b 100644
--- a/src/constants/vuetorrent/index.ts
+++ b/src/constants/vuetorrent/index.ts
@@ -5,7 +5,7 @@ import { DashboardDisplayMode } from './DashboardDisplayMode'
 import { DashboardProperty } from './DashboardProperty'
 import { DashboardPropertyType } from './DashboardPropertyType'
 import { FeedState } from './FeedState'
-import { getFileIcon, typesMap } from './FileIcon'
+import { FileType, extMap, typesMap } from './FileIcon'
 import { FilterType } from './FilterType'
 import { HistoryKey } from './HistoryKey'
 import { ThemeMode } from './ThemeMode'
@@ -27,8 +27,9 @@ export {
   DashboardProperty,
   DashboardPropertyType,
   FeedState,
-  getFileIcon,
+  FileType,
   typesMap,
+  extMap,
   FilterType,
   HistoryKey,
   ThemeMode,
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index 3cb3050d..aea95e13 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -3,7 +3,7 @@ import comparators, { Comparator, isObjectEqual } from './comparators'
 import { formatDataValue, formatDataUnit, formatData } from './data'
 import { QBIT_MAX_ETA, INFINITY_SYMBOL, formatEta, formatTimeMs, formatTimeSec, formatDuration } from './datetime'
 import { toPrecision, formatPercent } from './number'
-import { basename } from './path'
+import { basename, splitExt, getFileIcon, getExtType } from './path'
 import { formatSpeedValue, formatSpeedUnit, formatSpeed } from './speed'
 import { isWindows, isMac, doesCommand, openLink, downloadFile } from './system'
 import { titleCase, capitalize, extractHostname, getDomainBody, splitByUrl, containsUrl, isValidUri, codeToFlag } from './text'
@@ -27,6 +27,9 @@ export {
   toPrecision,
   formatPercent,
   basename,
+  splitExt,
+  getFileIcon,
+  getExtType,
   formatSpeedValue,
   formatSpeedUnit,
   formatSpeed,
diff --git a/src/helpers/path.spec.ts b/src/helpers/path.spec.ts
index 111bcb0f..e9f0daf5 100644
--- a/src/helpers/path.spec.ts
+++ b/src/helpers/path.spec.ts
@@ -1,4 +1,5 @@
-import { basename, splitExt } from './path'
+import { FileType } from '@/constants/vuetorrent'
+import { basename, splitExt, getExtType, getFileIcon } from './path'
 
 describe('helpers/path/basename', () => {
   test('*NIX :: should return basename on files', () => {
@@ -73,3 +74,47 @@ describe('helpers/path/splitExt', () => {
     expect(splitExt('.txt')).toEqual(['.txt', ''])
   })
 })
+
+describe('helpers/path/getExtType', () => {
+  it('should return the correct type for a known extension', () => {
+    expect(getExtType('txt')).toEqual(FileType.DOCUMENT)
+  })
+
+  it('should return unknown for an unknown extension', () => {
+    expect(getExtType('unknown')).toBe(FileType.UNKNOWN)
+  })
+
+  it('should return unknown for files without extension', () => {
+    expect(getExtType('file')).toBe(FileType.UNKNOWN)
+  })
+
+  it('should handle files starting with a dot', () => {
+    expect(getExtType('.hiddenfile')).toBe(FileType.UNKNOWN)
+  })
+})
+
+describe('helpers/path/getFileIcon', () => {
+  it('should return the correct icon for a known file type', () => {
+    expect(getFileIcon('file.txt')).toEqual('mdi-file-document')
+  })
+
+  it('should return the default icon for an unknown file type', () => {
+    expect(getFileIcon('file.unknown')).toEqual('mdi-file')
+  })
+
+  it('should handle filenames with multiple dots correctly', () => {
+    expect(getFileIcon('my.file.name.txt')).toEqual('mdi-file-document')
+  })
+
+  it('should return the default icon for files without extension', () => {
+    expect(getFileIcon('file')).toEqual('mdi-file')
+  })
+
+  it('should handle files starting with a dot', () => {
+    expect(getFileIcon('.hiddenfile')).toEqual('mdi-file')
+  })
+
+  it('should treat files with only extension as hidden', () => {
+    expect(getFileIcon('.txt')).toEqual('mdi-file')
+  })
+})
diff --git a/src/helpers/path.ts b/src/helpers/path.ts
index fa9044d3..39bc72f3 100644
--- a/src/helpers/path.ts
+++ b/src/helpers/path.ts
@@ -1,3 +1,5 @@
+import { extMap, FileType, typesMap } from '@/constants/vuetorrent'
+
 export function basename(path: string | null | undefined) {
   if (!path) return ''
 
@@ -17,3 +19,11 @@ export function splitExt(path: string | null | undefined): [string, string] {
   const ext = groups.pop()!
   return [groups.join('.'), ext]
 }
+
+export function getExtType(ext: string) {
+  return extMap[ext] || FileType.UNKNOWN
+}
+
+export function getFileIcon(filename: string) {
+  return typesMap[getExtType(splitExt(filename)[1])]
+}
diff --git a/src/locales/en.json b/src/locales/en.json
index f35078e3..bc3ab050 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -80,6 +80,19 @@
       "normal": "Normal",
       "unwanted": "Unwanted"
     },
+    "file_type": {
+      "archive": "Archives",
+      "audio": "Audio",
+      "book": "Books",
+      "document": "Documents",
+      "executable": "Executables",
+      "image": "Images",
+      "information": "Information",
+      "script": "Scripts",
+      "subtitle": "Subtitles",
+      "unknown": "Unknown",
+      "video": "Video"
+    },
     "filter_type": {
       "conjunctive": "Conjunctive filtering (AND)",
       "disjunctive": "Disjunctive filtering (OR)"