Feature: extract embedded cover art from audio files to display as thumbnails
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m28s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m28s
This commit is contained in:
parent
d982ce66a8
commit
ae6c97b9de
2 changed files with 23 additions and 1 deletions
|
|
@ -91,6 +91,8 @@ func (m *ThumbnailManager) worker(id int) {
|
||||||
err = generateImageThumbnail(fullSourcePath, destPath)
|
err = generateImageThumbnail(fullSourcePath, destPath)
|
||||||
} else if strings.HasPrefix(job.MimeType, "video/") {
|
} else if strings.HasPrefix(job.MimeType, "video/") {
|
||||||
err = generateVideoThumbnail(fullSourcePath, destPath)
|
err = generateVideoThumbnail(fullSourcePath, destPath)
|
||||||
|
} else if strings.HasPrefix(job.MimeType, "audio/") {
|
||||||
|
err = generateAudioThumbnail(fullSourcePath, destPath)
|
||||||
} else if strings.HasSuffix(job.FilePath, ".docx") || strings.HasSuffix(job.FilePath, ".pptx") || strings.HasSuffix(job.FilePath, ".xlsx") {
|
} else if strings.HasSuffix(job.FilePath, ".docx") || strings.HasSuffix(job.FilePath, ".pptx") || strings.HasSuffix(job.FilePath, ".xlsx") {
|
||||||
err = generateOfficeThumbnail(job.FilePath, destPath, m.Config)
|
err = generateOfficeThumbnail(job.FilePath, destPath, m.Config)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -135,6 +137,25 @@ func generateVideoThumbnail(src, dest string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateAudioThumbnail(src, dest string) error {
|
||||||
|
// Extract embedded cover art from audio file, scale to 256x256
|
||||||
|
cmd := exec.Command("ffmpeg", "-y", "-i", src, "-an", "-vcodec", "mjpeg", "-vframes", "1", "-vf", "scale=256:256:force_original_aspect_ratio=decrease", dest)
|
||||||
|
|
||||||
|
timer := time.AfterFunc(10*time.Second, func() {
|
||||||
|
if cmd.Process != nil {
|
||||||
|
cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ffmpeg audio cover extraction failed: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func generateOfficeThumbnail(filePath, destPath string, cfg *config.Config) error {
|
func generateOfficeThumbnail(filePath, destPath string, cfg *config.Config) error {
|
||||||
// Generate a download token for the Conversion API to fetch the file from our backend
|
// Generate a download token for the Conversion API to fetch the file from our backend
|
||||||
token, err := middleware.GenerateDownloadToken(0, "system_worker", cfg.JWTSecret)
|
token, err := middleware.GenerateDownloadToken(0, "system_worker", cfg.JWTSecret)
|
||||||
|
|
|
||||||
|
|
@ -1173,7 +1173,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
|
||||||
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext) || file.mime_type?.startsWith('image/');
|
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext) || file.mime_type?.startsWith('image/');
|
||||||
const isVideo = ['mp4', 'mkv', 'avi', 'mov', 'webm', 'ogg'].includes(ext) || file.mime_type?.startsWith('video/');
|
const isVideo = ['mp4', 'mkv', 'avi', 'mov', 'webm', 'ogg'].includes(ext) || file.mime_type?.startsWith('video/');
|
||||||
const isOffice = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(ext) || file.mime_type?.includes('officedocument');
|
const isOffice = ['docx', 'doc', 'pptx', 'ppt', 'xlsx', 'xls'].includes(ext) || file.mime_type?.includes('officedocument');
|
||||||
const isMedia = isImage || isVideo || isOffice;
|
const isAudio = ['mp3', 'flac', 'wav', 'm4a', 'wma', 'aac'].includes(ext) || file.mime_type?.startsWith('audio/');
|
||||||
|
const isMedia = isImage || isVideo || isOffice || isAudio;
|
||||||
|
|
||||||
if (ext === 'pdf') {
|
if (ext === 'pdf') {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Reference in a new issue