nkjoy commited on
Commit
f0c0241
·
verified ·
1 Parent(s): 0124722

이번엔 오디오플레이어 만들자 - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +231 -62
index.html CHANGED
@@ -790,70 +790,81 @@
790
 
791
  <!-- Enhanced Audio Player (Fixed at bottom) -->
792
  <div class="fixed bottom-0 left-0 right-0 bg-gradient-to-r from-purple-700 to-primary shadow-2xl z-50 border-t border-white border-opacity-20">
793
- <div class="max-w-7xl mx-auto px-4 py-2">
794
  <div class="flex items-center">
795
  <!-- Album Art -->
796
- <div class="flex-shrink-0 h-14 w-14 rounded-lg overflow-hidden shadow-lg relative group">
797
- <img src="https://image.yes24.com/goods/112163395/XL" alt="Current book cover" class="h-full w-full object-cover">
798
  <button class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
799
  <i class="fas fa-expand text-white text-lg"></i>
800
  </button>
801
  </div>
802
 
803
  <!-- Track Info -->
804
- <div class="ml-3 flex-1 min-w-0">
805
  <div class="flex items-center justify-between">
806
  <div class="truncate">
807
- <h3 class="text-sm font-medium text-white truncate">달러구트 꿈 백화점 1장 - 꿈 백화점에 오신 것을 환영합니다</h3>
808
- <p class="text-xs text-gray-200 truncate">이미예 | 7시간 12분 | 챕터 3/12</p>
809
  </div>
810
  <div class="flex items-center ml-2">
811
- <span class="text-xs text-gray-300">1:24:35</span>
812
  <span class="mx-1 text-xs text-gray-400">/</span>
813
- <span class="text-xs text-gray-300">7:12:08</span>
814
  </div>
815
  </div>
816
 
817
  <!-- Player Controls -->
818
  <div class="mt-1 flex items-center">
819
  <!-- Main Controls -->
820
- <div class="flex items-center space-x-2">
821
- <button class="text-gray-300 hover:text-white transition-colors" title="이전 챕터">
822
- <i class="fas fa-step-backward text-base"></i>
823
  </button>
824
- <button class="p-2 text-white bg-gradient-to-br from-secondary to-yellow-500 rounded-full shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200" title="재생/일시정지">
825
- <i class="fas fa-pause text-sm"></i>
826
  </button>
827
- <button class="text-gray-300 hover:text-white transition-colors" title="다음 챕터">
828
- <i class="fas fa-step-forward text-base"></i>
829
  </button>
830
  </div>
831
 
832
  <!-- Progress Bar -->
833
- <div class="ml-3 flex-1 relative">
834
  <div class="h-1 bg-gray-700 bg-opacity-60 rounded-full overflow-hidden">
835
- <div class="h-full bg-secondary rounded-full" style="width: 35%"></div>
836
  </div>
837
- <input type="range" min="0" max="100" value="35"
838
  class="absolute inset-0 w-full h-full opacity-0 cursor-pointer">
839
  </div>
840
 
841
  <!-- Secondary Controls -->
842
- <div class="ml-3 flex items-center space-x-3">
843
- <button class="text-gray-300 hover:text-white relative group" title="속도 조절">
844
- <i class="fas fa-tachometer-alt text-sm"></i>
845
- <div class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-24 bg-gray-800 text-white text-xs rounded py-1 px-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
846
- 재생속도: 1.0x
 
 
 
 
 
 
 
 
847
  </div>
848
- </button>
849
- <button class="text-gray-300 hover:text-white relative group" title="볼륨 조절">
850
- <i class="fas fa-volume-up text-sm"></i>
851
- <div class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-16 bg-gray-800 text-white text-xs rounded py-1 px-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
852
- 볼륨: 70%
 
 
 
853
  </div>
854
- </button>
855
- <button class="text-gray-300 hover:text-white" title="북마크">
856
- <i class="fas fa-bookmark text-sm"></i>
857
  </button>
858
  </div>
859
  </div>
@@ -863,44 +874,202 @@
863
  </div>
864
 
865
  <script>
866
- // Sample interactive elements
867
  document.addEventListener('DOMContentLoaded', function() {
868
- // Book card play button click event
869
- const playButtons = document.querySelectorAll('.book-card button');
870
- playButtons.forEach(button => {
871
- button.addEventListener('click', function(e) {
872
- e.preventDefault();
873
- alert('오디오북 샘플을 재생합니다! 실제 앱에서는 자동재생이 시작됩니다.');
874
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
  });
876
-
877
- // Mobile menu toggle (placeholder)
878
- const mobileMenuButton = document.querySelector('nav button:last-child');
879
- mobileMenuButton.addEventListener('click', function() {
880
- alert('모바일 메뉴가 열립니다! 실제 앱에서는 사이드 메뉴가 표시됩니다.');
 
 
 
881
  });
882
-
883
- // Subscription plan buttons
884
- const subscriptionButtons = document.querySelectorAll('.bg-white button');
885
- subscriptionButtons.forEach(button => {
886
- button.addEventListener('click', function() {
887
- const planName = this.closest('div').querySelector('h2').textContent;
888
- alert(`${planName} 플랜을 선택하셨습니다!`);
889
- });
890
  });
891
-
892
- // Audio player play/pause toggle
893
- const playerPlayButton = document.querySelector('.audio-player .fa-play').closest('button');
894
- playerPlayButton.addEventListener('click', function() {
895
- const icon = this.querySelector('i');
896
- if(icon.classList.contains('fa-play')) {
897
- icon.classList.remove('fa-play');
898
- icon.classList.add('fa-pause');
 
899
  } else {
900
- icon.classList.remove('fa-pause');
901
- icon.classList.add('fa-play');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
902
  }
903
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  });
905
  </script>
906
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nkjoy/audio" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
790
 
791
  <!-- Enhanced Audio Player (Fixed at bottom) -->
792
  <div class="fixed bottom-0 left-0 right-0 bg-gradient-to-r from-purple-700 to-primary shadow-2xl z-50 border-t border-white border-opacity-20">
793
+ <div class="max-w-7xl mx-auto px-4 py-3">
794
  <div class="flex items-center">
795
  <!-- Album Art -->
796
+ <div class="flex-shrink-0 h-16 w-16 rounded-lg overflow-hidden shadow-lg relative group">
797
+ <img src="https://image.yes24.com/goods/112163395/XL" alt="Current book cover" class="h-full w-full object-cover" id="currentBookCover">
798
  <button class="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">
799
  <i class="fas fa-expand text-white text-lg"></i>
800
  </button>
801
  </div>
802
 
803
  <!-- Track Info -->
804
+ <div class="ml-4 flex-1 min-w-0">
805
  <div class="flex items-center justify-between">
806
  <div class="truncate">
807
+ <h3 class="text-sm font-medium text-white truncate" id="currentTrackTitle">달러구트 꿈 백화점 1장 - 꿈 백화점에 오신 것을 환영합니다</h3>
808
+ <p class="text-xs text-gray-200 truncate" id="currentBookInfo">이미예 | 7시간 12분 | 챕터 3/12</p>
809
  </div>
810
  <div class="flex items-center ml-2">
811
+ <span class="text-xs text-gray-300" id="currentTime">1:24:35</span>
812
  <span class="mx-1 text-xs text-gray-400">/</span>
813
+ <span class="text-xs text-gray-300" id="duration">7:12:08</span>
814
  </div>
815
  </div>
816
 
817
  <!-- Player Controls -->
818
  <div class="mt-1 flex items-center">
819
  <!-- Main Controls -->
820
+ <div class="flex items-center space-x-3">
821
+ <button class="text-gray-300 hover:text-white transition-colors" title="이전 챕터" id="prevChapter">
822
+ <i class="fas fa-step-backward text-lg"></i>
823
  </button>
824
+ <button class="p-2 text-white bg-gradient-to-br from-secondary to-yellow-500 rounded-full shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200" title="재생/일시정지" id="playPauseBtn">
825
+ <i class="fas fa-pause text-base" id="playPauseIcon"></i>
826
  </button>
827
+ <button class="text-gray-300 hover:text-white transition-colors" title="다음 챕터" id="nextChapter">
828
+ <i class="fas fa-step-forward text-lg"></i>
829
  </button>
830
  </div>
831
 
832
  <!-- Progress Bar -->
833
+ <div class="ml-4 flex-1 relative">
834
  <div class="h-1 bg-gray-700 bg-opacity-60 rounded-full overflow-hidden">
835
+ <div class="h-full bg-secondary rounded-full" id="progressBar" style="width: 35%"></div>
836
  </div>
837
+ <input type="range" min="0" max="100" value="35" id="progressSlider"
838
  class="absolute inset-0 w-full h-full opacity-0 cursor-pointer">
839
  </div>
840
 
841
  <!-- Secondary Controls -->
842
+ <div class="ml-4 flex items-center space-x-4">
843
+ <div class="relative group" id="playbackSpeedContainer">
844
+ <button class="text-gray-300 hover:text-white" title="속도 조절" id="playbackSpeedBtn">
845
+ <i class="fas fa-tachometer-alt text-base"></i>
846
+ </button>
847
+ <div class="hidden group-hover:block absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-32 bg-gray-800 text-white text-sm rounded py-1 px-2 shadow-lg">
848
+ <div class="flex space-x-2">
849
+ <button class="speed-option" data-speed="0.5">0.5x</button>
850
+ <button class="speed-option" data-speed="0.75">0.75x</button>
851
+ <button class="speed-option active" data-speed="1.0">1.0x</button>
852
+ <button class="speed-option" data-speed="1.25">1.25x</button>
853
+ <button class="speed-option" data-speed="1.5">1.5x</button>
854
+ </div>
855
  </div>
856
+ </div>
857
+ <div class="relative group" id="volumeControlContainer">
858
+ <button class="text-gray-300 hover:text-white" title="볼륨 조절" id="volumeBtn">
859
+ <i class="fas fa-volume-up text-base"></i>
860
+ </button>
861
+ <div class="hidden group-hover:block absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 w-24 bg-gray-800 rounded py-2 px-3 shadow-lg">
862
+ <input type="range" min="0" max="100" value="70" id="volumeSlider"
863
+ class="w-full h-1 bg-gray-600 rounded-lg appearance-none cursor-pointer">
864
  </div>
865
+ </div>
866
+ <button class="text-gray-300 hover:text-white" title="북마크" id="bookmarkBtn">
867
+ <i class="fas fa-bookmark text-base"></i>
868
  </button>
869
  </div>
870
  </div>
 
874
  </div>
875
 
876
  <script>
 
877
  document.addEventListener('DOMContentLoaded', function() {
878
+ // Audio player state
879
+ const audioPlayer = {
880
+ isPlaying: false,
881
+ currentTime: 0,
882
+ duration: 0,
883
+ playbackRate: 1.0,
884
+ volume: 0.7,
885
+ currentChapter: 3,
886
+ totalChapters: 12
887
+ };
888
+
889
+ // DOM elements
890
+ const playPauseBtn = document.getElementById('playPauseBtn');
891
+ const playPauseIcon = document.getElementById('playPauseIcon');
892
+ const progressBar = document.getElementById('progressBar');
893
+ const progressSlider = document.getElementById('progressSlider');
894
+ const currentTimeEl = document.getElementById('currentTime');
895
+ const durationEl = document.getElementById('duration');
896
+ const volumeSlider = document.getElementById('volumeSlider');
897
+ const volumeBtn = document.getElementById('volumeBtn');
898
+ const playbackSpeedBtn = document.getElementById('playbackSpeedBtn');
899
+ const speedOptions = document.querySelectorAll('.speed-option');
900
+ const prevChapterBtn = document.getElementById('prevChapter');
901
+ const nextChapterBtn = document.getElementById('nextChapter');
902
+ const bookmarkBtn = document.getElementById('bookmarkBtn');
903
+ const currentTrackTitle = document.getElementById('currentTrackTitle');
904
+ const currentBookInfo = document.getElementById('currentBookInfo');
905
+ const currentBookCover = document.getElementById('currentBookCover');
906
+
907
+ // Initialize player
908
+ function initPlayer() {
909
+ updateTimeDisplay();
910
+ updateVolumeIcon();
911
+ }
912
+
913
+ // Play/Pause toggle
914
+ playPauseBtn.addEventListener('click', function() {
915
+ audioPlayer.isPlaying = !audioPlayer.isPlaying;
916
+ if (audioPlayer.isPlaying) {
917
+ playPauseIcon.classList.remove('fa-play');
918
+ playPauseIcon.classList.add('fa-pause');
919
+ // In a real app, you would call audioElement.play() here
920
+ } else {
921
+ playPauseIcon.classList.remove('fa-pause');
922
+ playPauseIcon.classList.add('fa-play');
923
+ // In a real app, you would call audioElement.pause() here
924
+ }
925
  });
926
+
927
+ // Progress slider
928
+ progressSlider.addEventListener('input', function() {
929
+ const percent = this.value;
930
+ progressBar.style.width = `${percent}%`;
931
+ // Calculate and update current time based on percentage
932
+ audioPlayer.currentTime = (percent / 100) * audioPlayer.duration;
933
+ updateTimeDisplay();
934
  });
935
+
936
+ // Volume control
937
+ volumeSlider.addEventListener('input', function() {
938
+ audioPlayer.volume = this.value / 100;
939
+ updateVolumeIcon();
940
+ // In a real app, you would set audioElement.volume here
 
 
941
  });
942
+
943
+ function updateVolumeIcon() {
944
+ const volIcon = volumeBtn.querySelector('i');
945
+ if (audioPlayer.volume === 0) {
946
+ volIcon.classList.remove('fa-volume-up', 'fa-volume-down');
947
+ volIcon.classList.add('fa-volume-mute');
948
+ } else if (audioPlayer.volume < 0.5) {
949
+ volIcon.classList.remove('fa-volume-up', 'fa-volume-mute');
950
+ volIcon.classList.add('fa-volume-down');
951
  } else {
952
+ volIcon.classList.remove('fa-volume-down', 'fa-volume-mute');
953
+ volIcon.classList.add('fa-volume-up');
954
+ }
955
+ }
956
+
957
+ // Playback speed control
958
+ speedOptions.forEach(option => {
959
+ option.addEventListener('click', function() {
960
+ const speed = parseFloat(this.dataset.speed);
961
+ audioPlayer.playbackRate = speed;
962
+
963
+ // Update active state
964
+ speedOptions.forEach(opt => opt.classList.remove('active'));
965
+ this.classList.add('active');
966
+
967
+ // In a real app, you would set audioElement.playbackRate here
968
+ });
969
+ });
970
+
971
+ // Chapter navigation
972
+ prevChapterBtn.addEventListener('click', function() {
973
+ if (audioPlayer.currentChapter > 1) {
974
+ audioPlayer.currentChapter--;
975
+ updateBookInfo();
976
+ resetProgress();
977
  }
978
  });
979
+
980
+ nextChapterBtn.addEventListener('click', function() {
981
+ if (audioPlayer.currentChapter < audioPlayer.totalChapters) {
982
+ audioPlayer.currentChapter++;
983
+ updateBookInfo();
984
+ resetProgress();
985
+ }
986
+ });
987
+
988
+ // Bookmark
989
+ bookmarkBtn.addEventListener('click', function() {
990
+ alert(`현재 ${formatTime(audioPlayer.currentTime)}에 북마크를 추가했습니다.`);
991
+ });
992
+
993
+ // Update time display
994
+ function updateTimeDisplay() {
995
+ currentTimeEl.textContent = formatTime(audioPlayer.currentTime);
996
+ durationEl.textContent = formatTime(audioPlayer.duration);
997
+ }
998
+
999
+ // Update book/chapter info
1000
+ function updateBookInfo() {
1001
+ currentBookInfo.textContent = `이미예 | 7시간 12분 | 챕터 ${audioPlayer.currentChapter}/${audioPlayer.totalChapters}`;
1002
+ }
1003
+
1004
+ // Reset progress when changing chapters
1005
+ function resetProgress() {
1006
+ audioPlayer.currentTime = 0;
1007
+ progressBar.style.width = '0%';
1008
+ progressSlider.value = 0;
1009
+ updateTimeDisplay();
1010
+ }
1011
+
1012
+ // Format time (HH:MM:SS)
1013
+ function formatTime(seconds) {
1014
+ const hrs = Math.floor(seconds / 3600);
1015
+ const mins = Math.floor((seconds % 3600) / 60);
1016
+ const secs = Math.floor(seconds % 60);
1017
+ return `${hrs}:${mins < 10 ? '0' : ''}${mins}:${secs < 10 ? '0' : ''}${secs}`;
1018
+ }
1019
+
1020
+ // Simulate playback progress (for demo)
1021
+ function simulatePlayback() {
1022
+ if (audioPlayer.isPlaying) {
1023
+ // Update current time (demo only - in real app use actual audio position)
1024
+ audioPlayer.currentTime += 0.5;
1025
+
1026
+ // Update progress
1027
+ const percent = (audioPlayer.currentTime / audioPlayer.duration) * 100;
1028
+ progressBar.style.width = `${percent}%`;
1029
+ progressSlider.value = percent;
1030
+ updateTimeDisplay();
1031
+
1032
+ // Check if chapter ended
1033
+ if (percent >= 100 && audioPlayer.currentChapter < audioPlayer.totalChapters) {
1034
+ audioPlayer.currentChapter++;
1035
+ updateBookInfo();
1036
+ audioPlayer.currentTime = 0;
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ // Initialize demo values
1042
+ audioPlayer.currentTime = 5065; // 1:24:25 in seconds
1043
+ audioPlayer.duration = 25920; // 7:12:00 in seconds
1044
+ initPlayer();
1045
+
1046
+ // Start playback simulation (for demo)
1047
+ setInterval(simulatePlayback, 500);
1048
+
1049
+ // Book card play buttons
1050
+ document.querySelectorAll('.book-card button').forEach(button => {
1051
+ button.addEventListener('click', function(e) {
1052
+ e.preventDefault();
1053
+ const bookCard = this.closest('.book-card');
1054
+ const title = bookCard.querySelector('h3').textContent;
1055
+ const author = bookCard.querySelector('p').textContent;
1056
+ const cover = bookCard.querySelector('img').src;
1057
+
1058
+ // Update player with new book
1059
+ currentTrackTitle.textContent = title;
1060
+ currentBookInfo.textContent = `${author} | 7시간 12분 | 챕터 1/12`;
1061
+ currentBookCover.src = cover;
1062
+
1063
+ // Reset player state
1064
+ audioPlayer.currentChapter = 1;
1065
+ audioPlayer.isPlaying = true;
1066
+ playPauseIcon.classList.remove('fa-play');
1067
+ playPauseIcon.classList.add('fa-pause');
1068
+ resetProgress();
1069
+
1070
+ alert(`${title} 오디오북 재생을 시작합니다!`);
1071
+ });
1072
+ });
1073
  });
1074
  </script>
1075
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nkjoy/audio" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>