awacke1 commited on
Commit
be7db56
·
verified ·
1 Parent(s): a9cfc9e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -0
app.py CHANGED
@@ -562,7 +562,236 @@ def display_settings_tab():
562
 
563
 
564
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
 
567
 
568
 
 
562
 
563
 
564
 
565
+ def get_download_link(file: str, file_type: str = "zip") -> str:
566
+ """
567
+ Convert a file to base64 and return an HTML link for download.
568
+ Supports multiple file types with appropriate MIME types.
569
+ """
570
+ try:
571
+ with open(file, "rb") as f:
572
+ b64 = base64.b64encode(f.read()).decode()
573
+
574
+ # Get filename for display
575
+ filename = os.path.basename(file)
576
+
577
+ # Define MIME types and emoji icons for different file types
578
+ mime_types = {
579
+ "zip": ("application/zip", "📂"),
580
+ "mp3": ("audio/mpeg", "🎵"),
581
+ "wav": ("audio/wav", "🔊"),
582
+ "md": ("text/markdown", "📝"),
583
+ "pdf": ("application/pdf", "📄"),
584
+ "txt": ("text/plain", "📋"),
585
+ "json": ("application/json", "📊"),
586
+ "csv": ("text/csv", "📈"),
587
+ "png": ("image/png", "🖼"),
588
+ "jpg": ("image/jpeg", "🖼"),
589
+ "jpeg": ("image/jpeg", "🖼")
590
+ }
591
+
592
+ # Get MIME type and emoji for file
593
+ mime_type, emoji = mime_types.get(
594
+ file_type.lower(),
595
+ ("application/octet-stream", "⬇️")
596
+ )
597
+
598
+ # Create download link with appropriate MIME type
599
+ link = f'<a href="data:{mime_type};base64,{b64}" download="{filename}">{emoji} Download {filename}</a>'
600
+
601
+ return link
602
+
603
+ except FileNotFoundError:
604
+ return f"<p style='color: red'>❌ File not found: {file}</p>"
605
+ except Exception as e:
606
+ return f"<p style='color: red'>❌ Error creating download link: {str(e)}</p>"
607
+
608
+ def play_and_download_audio(file_path: str, file_type: str = "mp3"):
609
+ """
610
+ Display audio player and download link for audio file.
611
+ Includes error handling and file validation.
612
+ """
613
+ if not file_path:
614
+ st.warning("No audio file provided.")
615
+ return
616
+
617
+ if not os.path.exists(file_path):
618
+ st.error(f"Audio file not found: {file_path}")
619
+ return
620
+
621
+ try:
622
+ # Display audio player
623
+ st.audio(file_path)
624
+
625
+ # Create and display download link
626
+ dl_link = get_download_link(file_path, file_type=file_type)
627
+ st.markdown(dl_link, unsafe_allow_html=True)
628
+
629
+ except Exception as e:
630
+ st.error(f"Error playing audio: {str(e)}")
631
 
632
+ def get_file_info(file_path: str) -> dict:
633
+ """
634
+ Get detailed information about a file.
635
+ Returns dictionary with size, modification time, and other metadata.
636
+ """
637
+ try:
638
+ stats = os.stat(file_path)
639
+
640
+ # Get basic file information
641
+ info = {
642
+ 'name': os.path.basename(file_path),
643
+ 'path': file_path,
644
+ 'size': stats.st_size,
645
+ 'modified': datetime.fromtimestamp(stats.st_mtime),
646
+ 'created': datetime.fromtimestamp(stats.st_ctime),
647
+ 'type': os.path.splitext(file_path)[1].lower().strip('.'),
648
+ }
649
+
650
+ # Add formatted size
651
+ if info['size'] < 1024:
652
+ info['size_fmt'] = f"{info['size']} B"
653
+ elif info['size'] < 1024 * 1024:
654
+ info['size_fmt'] = f"{info['size']/1024:.1f} KB"
655
+ else:
656
+ info['size_fmt'] = f"{info['size']/(1024*1024):.1f} MB"
657
+
658
+ # Add formatted dates
659
+ info['modified_fmt'] = info['modified'].strftime("%Y-%m-%d %H:%M:%S")
660
+ info['created_fmt'] = info['created'].strftime("%Y-%m-%d %H:%M:%S")
661
+
662
+ return info
663
+
664
+ except Exception as e:
665
+ st.error(f"Error getting file info: {str(e)}")
666
+ return None
667
+
668
+ def sanitize_filename(filename: str) -> str:
669
+ """
670
+ Clean and sanitize a filename to ensure it's safe for filesystem.
671
+ Removes/replaces unsafe characters and enforces length limits.
672
+ """
673
+ # Remove or replace unsafe characters
674
+ filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
675
+
676
+ # Remove leading/trailing spaces and dots
677
+ filename = filename.strip('. ')
678
+
679
+ # Limit length (reserving space for extension)
680
+ max_length = 255
681
+ name, ext = os.path.splitext(filename)
682
+ if len(filename) > max_length:
683
+ return name[:(max_length-len(ext))] + ext
684
+
685
+ return filename
686
+
687
+ def create_file_with_metadata(filename: str, content: str, metadata: dict = None):
688
+ """
689
+ Create a file with optional metadata header.
690
+ Useful for storing additional information with files.
691
+ """
692
+ try:
693
+ # Sanitize filename
694
+ safe_filename = sanitize_filename(filename)
695
+
696
+ # Ensure directory exists
697
+ os.makedirs(os.path.dirname(safe_filename) or '.', exist_ok=True)
698
+
699
+ # Prepare content with metadata
700
+ if metadata:
701
+ metadata_str = json.dumps(metadata, indent=2)
702
+ full_content = f"""---
703
+ {metadata_str}
704
+ ---
705
+ {content}"""
706
+ else:
707
+ full_content = content
708
+
709
+ # Write file
710
+ with open(safe_filename, 'w', encoding='utf-8') as f:
711
+ f.write(full_content)
712
+
713
+ return safe_filename
714
+
715
+ except Exception as e:
716
+ st.error(f"Error creating file: {str(e)}")
717
+ return None
718
+
719
+ def read_file_with_metadata(filename: str) -> tuple:
720
+ """
721
+ Read a file and extract any metadata header.
722
+ Returns tuple of (content, metadata).
723
+ """
724
+ try:
725
+ with open(filename, 'r', encoding='utf-8') as f:
726
+ content = f.read()
727
+
728
+ # Check for metadata section
729
+ if content.startswith('---\n'):
730
+ # Find end of metadata section
731
+ end_meta = content.find('\n---\n', 4)
732
+ if end_meta != -1:
733
+ try:
734
+ metadata = json.loads(content[4:end_meta])
735
+ content = content[end_meta+5:]
736
+ return content, metadata
737
+ except json.JSONDecodeError:
738
+ pass
739
+
740
+ return content, None
741
+
742
+ except Exception as e:
743
+ st.error(f"Error reading file: {str(e)}")
744
+ return None, None
745
+
746
+ def archive_files(file_paths: list, archive_name: str = None) -> str:
747
+ """
748
+ Create a zip archive containing the specified files.
749
+ Returns path to created archive.
750
+ """
751
+ try:
752
+ # Generate archive name if not provided
753
+ if not archive_name:
754
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
755
+ archive_name = f"archive_{timestamp}.zip"
756
+
757
+ # Create zip file
758
+ with zipfile.ZipFile(archive_name, 'w', zipfile.ZIP_DEFLATED) as zf:
759
+ for file_path in file_paths:
760
+ if os.path.exists(file_path):
761
+ zf.write(file_path, os.path.basename(file_path))
762
+
763
+ return archive_name
764
+
765
+ except Exception as e:
766
+ st.error(f"Error creating archive: {str(e)}")
767
+ return None
768
+
769
+ def list_files_by_type(directory: str = ".",
770
+ extensions: list = None,
771
+ recursive: bool = False) -> dict:
772
+ """
773
+ List files in directory filtered by extension.
774
+ Returns dict grouping files by type.
775
+ """
776
+ try:
777
+ if extensions is None:
778
+ extensions = ['md', 'mp3', 'wav', 'pdf', 'txt', 'json', 'csv']
779
+
780
+ files = {}
781
+ pattern = "**/*" if recursive else "*"
782
+
783
+ for ext in extensions:
784
+ glob_pattern = f"{pattern}.{ext}"
785
+ matches = glob.glob(os.path.join(directory, glob_pattern),
786
+ recursive=recursive)
787
+ if matches:
788
+ files[ext] = matches
789
+
790
+ return files
791
+
792
+ except Exception as e:
793
+ st.error(f"Error listing files: {str(e)}")
794
+ return {}
795
 
796
 
797