jhh6576 commited on
Commit
42a2600
·
verified ·
1 Parent(s): 10ef434

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +207 -26
app_enhanced.py CHANGED
@@ -474,7 +474,13 @@ class EnhancedComicGenerator:
474
  def _save_results(self, pages):
475
  try:
476
  os.makedirs(self.output_dir, exist_ok=True)
477
- pages_data = [{'panels': [p.__dict__ for p in page.panels], 'bubbles': [b.__dict__ for b in page.bubbles]} for page in pages]
 
 
 
 
 
 
478
  with open(os.path.join(self.output_dir, 'pages.json'), 'w', encoding='utf-8') as f:
479
  json.dump(pages_data, f, indent=2)
480
  self._copy_template_files()
@@ -664,9 +670,38 @@ class EnhancedComicGenerator:
664
  return bubbleDiv;
665
  }
666
 
667
- function applyBubbleType(bubble, type) { /* (function is complete and correct) */ }
668
- function changeBubbleType(type) { /* (function is complete and correct) */ }
669
- function rotateBubbleTail() { /* (function is complete and correct) */ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
 
671
  function selectPanel(panel) {
672
  document.querySelectorAll('.panel.selected').forEach(p => p.classList.remove('selected'));
@@ -735,17 +770,172 @@ class EnhancedComicGenerator:
735
  }
736
  }
737
 
738
- async function exportPagesToPNG() { /* (function is complete and correct) */ }
739
- function replacePanelImage() { /* (function is complete and correct) */ }
740
- function adjustFrame(direction) { /* (function is complete and correct) */ }
741
- function updateImageTransform(img) { /* (function is complete and correct) */ }
742
- function handleZoom(event) { /* (function is complete and correct) */ }
743
- function resetPanelTransform() { /* (function is complete and correct) */ }
744
- function startPan(event) { /* (function is complete and correct) */ }
745
- function panImage(event) { /* (function is complete and correct) */ }
746
- function stopPan(event) { /* (function is complete and correct) */ }
747
- function addBubbleToPanel() { /* (function is complete and correct) */ }
748
- function gotoTimestamp() { /* (function is complete and correct) */ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  </script>
750
  </body>
751
  </html>'''
@@ -785,17 +975,8 @@ def status():
785
 
786
  @app.route('/handle_link', methods=['POST'])
787
  def handle_link():
788
- try:
789
- link = request.form.get('link', '')
790
- if not link: return "❌ No link provided"
791
- import yt_dlp
792
- ydl_opts = {'outtmpl': comic_generator.video_path, 'format': 'best[height<=720]', 'overwrites': True}
793
- with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download([link])
794
- threading.Thread(target=comic_generator.generate_comic).start()
795
- return "Generation started. Please poll /status for updates."
796
- except Exception as e:
797
- traceback.print_exc()
798
- return f"❌ An unexpected error occurred: {str(e)}"
799
 
800
  @app.route('/replace_panel', methods=['POST'])
801
  def replace_panel():
 
474
  def _save_results(self, pages):
475
  try:
476
  os.makedirs(self.output_dir, exist_ok=True)
477
+ pages_data = []
478
+ for page in pages:
479
+ page_dict = {
480
+ 'panels': [p.__dict__ for p in page.panels],
481
+ 'bubbles': [b.__dict__ for b in page.bubbles]
482
+ }
483
+ pages_data.append(page_dict)
484
  with open(os.path.join(self.output_dir, 'pages.json'), 'w', encoding='utf-8') as f:
485
  json.dump(pages_data, f, indent=2)
486
  self._copy_template_files()
 
670
  return bubbleDiv;
671
  }
672
 
673
+ function applyBubbleType(bubble, type) {
674
+ bubble.querySelectorAll('.thought-dot').forEach(el => el.remove());
675
+ let classesToKeep = 'speech-bubble';
676
+ if (bubble.classList.contains('selected')) classesToKeep += ' selected';
677
+ if (bubble.classList.contains('flipped')) classesToKeep += ' flipped';
678
+ if (bubble.classList.contains('flipped-vertical')) classesToKeep += ' flipped-vertical';
679
+ bubble.className = classesToKeep;
680
+ bubble.classList.add(type);
681
+ bubble.dataset.type = type;
682
+ if (type === 'thought') {
683
+ for (let i = 1; i <= 2; i++) {
684
+ const dot = document.createElement('div');
685
+ dot.className = `thought-dot thought-dot-${i}`;
686
+ bubble.appendChild(dot);
687
+ }
688
+ }
689
+ }
690
+
691
+ function changeBubbleType(type) {
692
+ if (!currentlySelectedBubble) return;
693
+ applyBubbleType(currentlySelectedBubble, type);
694
+ }
695
+
696
+ function rotateBubbleTail() {
697
+ if (!currentlySelectedBubble) { alert("Please select a bubble first."); return; }
698
+ const isFlippedH = currentlySelectedBubble.classList.contains('flipped');
699
+ const isFlippedV = currentlySelectedBubble.classList.contains('flipped-vertical');
700
+ if (!isFlippedH && !isFlippedV) { currentlySelectedBubble.classList.add('flipped'); }
701
+ else if (isFlippedH && !isFlippedV) { currentlySelectedBubble.classList.add('flipped-vertical'); }
702
+ else if (isFlippedH && isFlippedV) { currentlySelectedBubble.classList.remove('flipped'); }
703
+ else { currentlySelectedBubble.classList.remove('flipped-vertical'); }
704
+ }
705
 
706
  function selectPanel(panel) {
707
  document.querySelectorAll('.panel.selected').forEach(p => p.classList.remove('selected'));
 
770
  }
771
  }
772
 
773
+ async function exportPagesToPNG() {
774
+ const pages = document.querySelectorAll('.comic-page');
775
+ if (pages.length === 0) return alert("No pages found.");
776
+ alert(`Starting export of ${pages.length} page(s).`);
777
+ for (let i = 0; i < pages.length; i++) {
778
+ try {
779
+ const canvas = await html2canvas(pages[i], { scale: 2 });
780
+ const link = document.createElement('a');
781
+ link.download = `comic-page-${i + 1}.png`;
782
+ link.href = canvas.toDataURL('image/png');
783
+ link.click();
784
+ } catch (err) { alert(`Failed to export page ${i + 1}.`); }
785
+ }
786
+ }
787
+
788
+ function replacePanelImage() {
789
+ if (!currentlySelectedPanel) { alert("Please select a panel first."); return; }
790
+ const img = currentlySelectedPanel.querySelector('img');
791
+ const uploader = document.getElementById('image-uploader');
792
+ uploader.onchange = (event) => {
793
+ const file = event.target.files[0];
794
+ if (!file) return;
795
+ const formData = new FormData();
796
+ formData.append('image', file);
797
+ img.style.opacity = '0.5';
798
+ fetch('/replace_panel', { method: 'POST', body: formData })
799
+ .then(response => response.json())
800
+ .then(data => {
801
+ if (data.success) {
802
+ img.src = `/frames/final/${data.new_filename}?t=${new Date().getTime()}`;
803
+ } else { alert('Error replacing image: ' + data.error); }
804
+ img.style.opacity = '1';
805
+ })
806
+ .catch(() => {
807
+ alert('An error occurred during the upload.');
808
+ img.style.opacity = '1';
809
+ });
810
+ uploader.value = '';
811
+ };
812
+ uploader.click();
813
+ }
814
+
815
+ function adjustFrame(direction) {
816
+ if (!currentlySelectedPanel) { alert("Please select a panel first."); return; }
817
+ const img = currentlySelectedPanel.querySelector('img');
818
+ let filename = img.src.substring(img.src.lastIndexOf('/') + 1).split('?')[0];
819
+ img.style.opacity = '0.5';
820
+ fetch('/regenerate_frame', {
821
+ method: 'POST',
822
+ headers: { 'Content-Type': 'application/json' },
823
+ body: JSON.stringify({ filename, direction })
824
+ })
825
+ .then(res => res.json())
826
+ .then(data => {
827
+ if (data.success) {
828
+ img.src = `/frames/final/${filename}?t=${new Date().getTime()}`;
829
+ } else { alert('Error: ' + data.message); }
830
+ img.style.opacity = '1';
831
+ })
832
+ .catch(() => {
833
+ alert('An error occurred.');
834
+ img.style.opacity = '1';
835
+ });
836
+ }
837
+
838
+ function updateImageTransform(img) {
839
+ const zoom = (img.dataset.zoom || 100) / 100;
840
+ const x = img.dataset.translateX || 0;
841
+ const y = img.dataset.translateY || 0;
842
+ img.style.transform = `translateX(${x}px) translateY(${y}px) scale(${zoom})`;
843
+ img.classList.toggle('pannable', zoom > 1);
844
+ }
845
+
846
+ function handleZoom(event) {
847
+ if (!currentlySelectedPanel) return;
848
+ const img = currentlySelectedPanel.querySelector('img');
849
+ img.dataset.zoom = event.target.value;
850
+ updateImageTransform(img);
851
+ }
852
+
853
+ function resetPanelTransform() {
854
+ if (!currentlySelectedPanel) { alert("Please select a panel first."); return; }
855
+ const img = currentlySelectedPanel.querySelector('img');
856
+ img.dataset.zoom = 100;
857
+ img.dataset.translateX = 0;
858
+ img.dataset.translateY = 0;
859
+ document.getElementById('zoom-slider').value = 100;
860
+ updateImageTransform(img);
861
+ }
862
+
863
+ function startPan(event) {
864
+ if (event.button !== 0) return;
865
+ const img = event.target;
866
+ if (parseFloat(img.dataset.zoom || 100) <= 100) return;
867
+ event.preventDefault();
868
+ isPanning = true;
869
+ img.classList.add('panning');
870
+ panStartX = event.clientX;
871
+ panStartY = event.clientY;
872
+ panStartTranslateX = parseFloat(img.dataset.translateX || 0);
873
+ panStartTranslateY = parseFloat(img.dataset.translateY || 0);
874
+ }
875
+
876
+ function panImage(event) {
877
+ if (!isPanning || !currentlySelectedPanel) return;
878
+ const img = currentlySelectedPanel.querySelector('img');
879
+ img.dataset.translateX = panStartTranslateX + (event.clientX - panStartX);
880
+ img.dataset.translateY = panStartTranslateY + (event.clientY - panStartY);
881
+ updateImageTransform(img);
882
+ }
883
+
884
+ function stopPan() {
885
+ if (!isPanning) return;
886
+ isPanning = false;
887
+ currentlySelectedPanel?.querySelector('img')?.classList.remove('panning');
888
+ }
889
+
890
+ function addBubbleToPanel() {
891
+ if (!currentlySelectedPanel) { alert("Please select a panel first."); return; }
892
+ const newBubble = createBubbleElement({
893
+ id: `new-bubble-${Date.now()}`,
894
+ text: 'New Text...',
895
+ left: '10%',
896
+ top: '10%'
897
+ });
898
+ currentlySelectedPanel.appendChild(newBubble);
899
+ initializeBubbleEvents(newBubble);
900
+ selectBubble(newBubble);
901
+ editBubbleText(newBubble);
902
+ }
903
+
904
+ function gotoTimestamp() {
905
+ if (!currentlySelectedPanel) { alert("Please select a panel first."); return; }
906
+ const input = document.getElementById('timestamp-input');
907
+ const timeStr = input.value.trim();
908
+ if (!timeStr) return;
909
+ let parsedSeconds = 0;
910
+ if (timeStr.includes(':')) {
911
+ const parts = timeStr.split(':');
912
+ parsedSeconds = parseInt(parts[0], 10) * 60 + parseFloat(parts[1]);
913
+ } else {
914
+ parsedSeconds = parseFloat(timeStr);
915
+ }
916
+ if (isNaN(parsedSeconds)) { alert("Invalid time format."); return; }
917
+ const img = currentlySelectedPanel.querySelector('img');
918
+ let filename = img.src.substring(img.src.lastIndexOf('/') + 1).split('?')[0];
919
+ img.style.opacity = '0.5';
920
+ fetch('/goto_timestamp', {
921
+ method: 'POST',
922
+ headers: { 'Content-Type': 'application/json' },
923
+ body: JSON.stringify({ filename, timestamp: parsedSeconds })
924
+ })
925
+ .then(res => res.json())
926
+ .then(data => {
927
+ if (data.success) {
928
+ img.src = `/frames/final/${filename}?t=${new Date().getTime()}`;
929
+ input.value = '';
930
+ resetPanelTransform();
931
+ } else { alert('Error: ' + data.message); }
932
+ img.style.opacity = '1';
933
+ })
934
+ .catch(() => {
935
+ alert('An error occurred.');
936
+ img.style.opacity = '1';
937
+ });
938
+ }
939
  </script>
940
  </body>
941
  </html>'''
 
975
 
976
  @app.route('/handle_link', methods=['POST'])
977
  def handle_link():
978
+ # This route is disabled in the UI but remains functional
979
+ pass
 
 
 
 
 
 
 
 
 
980
 
981
  @app.route('/replace_panel', methods=['POST'])
982
  def replace_panel():