junaidbashir392 commited on
Commit
cf686ab
·
verified ·
1 Parent(s): a45459f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -188
app.py CHANGED
@@ -167,9 +167,7 @@ Examples:
167
  except Exception as e:
168
  print(f"Gemini global title parsing error: {e}")
169
  return None
170
-
171
  def extract_person_from_description_with_gemini(self, description: str) -> Optional[str]:
172
- """Extract a person's name from the description using Gemini (global, priority 2)"""
173
  if not description or len(description.strip()) < 10 or not model:
174
  return None
175
 
@@ -703,104 +701,29 @@ Note: Do not restrict by nationality, era, or field. Consider notable people wor
703
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
704
  <title>Recent Videos - {time_range_text}</title>
705
  <style>
706
- body {{ font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }}
707
- .container {{ max-width: 1200px; margin: 0 auto; }}
708
- h1 {{ color: #333; text-align: center; margin-bottom: 30px; }}
709
- .stats-info {{
710
- background: #e3f2fd;
711
- padding: 15px;
712
- border-radius: 8px;
713
- margin-bottom: 20px;
714
- text-align: center;
715
- font-size: 16px;
716
- color: #1976d2;
717
- }}
718
- .video-item {{
719
- display: flex;
720
- align-items: flex-start;
721
- background: white;
722
- margin-bottom: 15px;
723
- padding: 15px;
724
- border-radius: 8px;
725
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
726
- border-left: 4px solid #ccc;
727
- }}
728
- .video-item.Critical {{ border-left-color: #f44336; background-color: #ffebee; }}
729
- .video-item.Important {{ border-left-color: #ff9800; background-color: #fff3e0; }}
730
- .thumbnail {{
731
- width: 160px;
732
- height: 90px;
733
- object-fit: cover;
734
- margin-right: 15px;
735
- border-radius: 4px;
736
- flex-shrink: 0;
737
- }}
738
- .video-info {{ flex: 1; }}
739
- .video-title {{
740
- font-weight: bold;
741
- margin-bottom: 8px;
742
- font-size: 16px;
743
- line-height: 1.4;
744
- }}
745
- .video-meta {{
746
- color: #666;
747
- font-size: 14px;
748
- line-height: 1.6;
749
- margin-bottom: 5px;
750
- }}
751
- .channel-info {{
752
- display: flex;
753
- align-items: center;
754
- margin-bottom: 8px;
755
- }}
756
- .channel-icon {{
757
- width: 24px;
758
- height: 24px;
759
- border-radius: 50%;
760
- margin-right: 8px;
761
- }}
762
- .stats {{ color: #2196f3; font-weight: bold; }}
763
- .importance-badge {{
764
- display: inline-block;
765
- padding: 2px 8px;
766
- border-radius: 12px;
767
- font-size: 12px;
768
- font-weight: bold;
769
- margin-left: 10px;
770
- }}
771
- .importance-badge.Critical {{ background: #f44336; color: white; }}
772
- .importance-badge.Important {{ background: #ff9800; color: white; }}
773
- .importance-badge.Normal {{ background: #4caf50; color: white; }}
774
- .detection-badge {{
775
- display: inline-block;
776
- padding: 2px 6px;
777
- border-radius: 8px;
778
- font-size: 11px;
779
- background: #2196f3;
780
- color: white;
781
- margin-left: 5px;
782
- }}
783
- .video-links {{ margin-top: 8px; }}
784
- .video-links a {{
785
- display: inline-block;
786
- margin-right: 15px;
787
- color: #1976d2;
788
- text-decoration: none;
789
- font-size: 14px;
790
- padding: 4px 8px;
791
- border: 1px solid #1976d2;
792
- border-radius: 4px;
793
- transition: background-color 0.3s;
794
- }}
795
- .video-links a:hover {{
796
- background-color: #e3f2fd;
797
- }}
798
- .no-videos {{
799
- text-align: center;
800
- padding: 40px;
801
- color: #666;
802
- font-size: 16px;
803
- }}
804
  </style>
805
  </head>
806
  <body>
@@ -896,57 +819,30 @@ Note: Do not restrict by nationality, era, or field. Consider notable people wor
896
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
897
  <title>YouTube Competitor Analysis Dashboard (Global)</title>
898
  <style>
899
- body { font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
900
- .container { max-width: 1200px; margin: 0 auto; }
901
- h1 { color: #333; text-align: center; margin-bottom: 30px; }
902
- h2 { border-bottom: 2px solid #eee; padding-bottom: 10px; }
903
- .section { background: white; margin-bottom: 30px; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
904
- .trend-item { border: 1px solid #ddd; margin-bottom: 15px; padding: 15px; border-radius: 5px; background: #fafafa; }
905
- .trend-title { font-size: 18px; font-weight: bold; color: #d32f2f; margin-bottom: 10px; }
906
- .trend-meta { font-size: 14px; color: #666; margin-bottom: 5px; }
907
- .video-item { display: flex; align-items: flex-start; margin-bottom: 15px; padding: 10px; border-left: 4px solid #ccc; }
908
- .video-item.Critical { border-left-color: #f44336; background-color: #ffebee; }
909
- .video-item.Important { border-left-color: #ff9800; background-color: #fff3e0; }
910
- .thumbnail { width: 160px; height: 90px; object-fit: cover; margin-right: 15px; border-radius: 4px; flex-shrink: 0; }
911
- .video-info { flex: 1; }
912
- .video-title { font-weight: bold; margin-bottom: 5px; font-size: 16px; }
913
- .video-meta { color: #666; font-size: 14px; line-height: 1.6; }
914
- .importance-badge {
915
- display: inline-block;
916
- padding: 2px 8px;
917
- border-radius: 12px;
918
- font-size: 12px;
919
- font-weight: bold;
920
- margin-left: 10px;
921
- }
922
- .importance-badge.Critical { background: #f44336; color: white; }
923
- .importance-badge.Important { background: #ff9800; color: white; }
924
- .importance-badge.Normal { background: #4caf50; color: white; }
925
- .detection-badge {
926
- display: inline-block;
927
- padding: 2px 6px;
928
- border-radius: 8px;
929
- font-size: 11px;
930
- background: #2196f3;
931
- color: white;
932
- margin-left: 5px;
933
- }
934
- .stats { color: #2196f3; font-weight: bold; }
935
- .video-links { margin-top: 8px; }
936
- .video-links a {
937
- display: inline-block;
938
- margin-right: 15px;
939
- color: #1976d2;
940
- text-decoration: none;
941
- font-size: 14px;
942
- padding: 4px 8px;
943
- border: 1px solid #1976d2;
944
- border-radius: 4px;
945
- transition: background-color 0.3s;
946
- }
947
- .video-links a:hover {
948
- background-color: #e3f2fd;
949
- }
950
  </style>
951
  </head>
952
  <body>
@@ -1031,42 +927,19 @@ Note: Do not restrict by nationality, era, or field. Consider notable people wor
1031
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1032
  <title>Channel Management (Global)</title>
1033
  <style>
1034
- body { font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
1035
- .container { max-width: 800px; margin: 0 auto; }
1036
- h2 { border-bottom: 2px solid #eee; padding-bottom: 10px; }
1037
- .channel-item {
1038
- display: flex;
1039
- align-items: center;
1040
- background: white;
1041
- margin-bottom: 15px;
1042
- padding: 15px;
1043
- border-radius: 8px;
1044
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1045
- }
1046
- .channel-icon {
1047
- width: 48px;
1048
- height: 48px;
1049
- border-radius: 50%;
1050
- margin-right: 15px;
1051
- object-fit: cover;
1052
- }
1053
- .channel-info { flex: 1; }
1054
- .channel-name { font-weight: bold; font-size: 16px; margin-bottom: 5px; }
1055
- .channel-meta { color: #666; font-size: 14px; }
1056
- .channel-actions { display: flex; gap: 10px; }
1057
- .btn {
1058
- padding: 6px 12px;
1059
- border: none;
1060
- border-radius: 4px;
1061
- cursor: pointer;
1062
- font-size: 12px;
1063
- text-decoration: none;
1064
- display: inline-block;
1065
- transition: opacity 0.3s;
1066
- }
1067
- .btn-edit { background: #2196f3; color: white; }
1068
- .btn-delete { background: #f44336; color: white; }
1069
- .btn:hover { opacity: 0.8; }
1070
  </style>
1071
  </head>
1072
  <body>
@@ -1125,6 +998,42 @@ Note: Do not restrict by nationality, era, or field. Consider notable people wor
1125
  # アプリのインスタンスを作成
1126
  analyzer = YouTubeCompetitorAnalyzer()
1127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1128
  # Gradio インターface
1129
  def add_channel_interface(channel_ids_text):
1130
  """Interface function that supports adding multiple channel IDs"""
@@ -1187,7 +1096,7 @@ def show_recent_videos_interface(hours_selection, limit_selection):
1187
  return analyzer.generate_recent_videos_html(hours, limit)
1188
 
1189
  # Gradioアプリの構築
1190
- with gr.Blocks(title="YouTube Competitor Analysis (Global)", theme=gr.themes.Soft()) as app:
1191
  gr.Markdown("# 🌍 YouTube Competitor Analysis App (Global)")
1192
  gr.Markdown("Analyze competitor channel uploads and detect global clustered trends using **Gemini 2.5 Flash**.")
1193
 
 
167
  except Exception as e:
168
  print(f"Gemini global title parsing error: {e}")
169
  return None
 
170
  def extract_person_from_description_with_gemini(self, description: str) -> Optional[str]:
 
171
  if not description or len(description.strip()) < 10 or not model:
172
  return None
173
 
 
701
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
702
  <title>Recent Videos - {time_range_text}</title>
703
  <style>
704
+ body {{ font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #000000 !important; color: #ffffff !important; }}
705
+ .container {{ max-width: 1200px; margin: 0 auto; background-color: transparent !important; }}
706
+ h1 {{ color: #ffffff !important; text-align: center; margin-bottom: 30px; }}
707
+ .stats-info {{ background: rgba(255,255,255,0.03) !important; padding: 15px !important; border-radius: 8px !important; margin-bottom: 20px !important; text-align: center !important; font-size: 16px !important; color: #bcdffb !important; }}
708
+ .video-item {{ display: flex !important; align-items: flex-start !important; background: #0b0b0b !important; margin-bottom: 15px !important; padding: 15px !important; border-radius: 8px !important; box-shadow: 0 2px 6px rgba(0,0,0,0.6) !important; border-left: 4px solid #222 !important; }}
709
+ .video-item.Critical {{ border-left-color: #b71c1c !important; background-color: #120202 !important; }}
710
+ .video-item.Important {{ border-left-color: #bf360c !important; background-color: #241100 !important; }}
711
+ .thumbnail {{ width: 160px !important; height: 90px !important; object-fit: cover !important; margin-right: 15px !important; border-radius: 4px !important; flex-shrink: 0 !important; }}
712
+ .video-info {{ flex: 1 !important; }}
713
+ .video-title {{ font-weight: bold !important; margin-bottom: 8px !important; font-size: 16px !important; line-height: 1.4 !important; color: #ffffff !important; }}
714
+ .video-meta {{ color: #cfcfcf !important; font-size: 14px !important; line-height: 1.6 !important; margin-bottom: 5px !important; }}
715
+ .channel-info {{ display: flex !important; align-items: center !important; margin-bottom: 8px !important; }}
716
+ .channel-icon {{ width: 24px !important; height: 24px !important; border-radius: 50% !important; margin-right: 8px !important; }}
717
+ .stats {{ color: #64b5f6 !important; font-weight: bold !important; }}
718
+ .importance-badge {{ display: inline-block !important; padding: 2px 8px !important; border-radius: 12px !important; font-size: 12px !important; font-weight: bold !important; margin-left: 10px !important; }}
719
+ .importance-badge.Critical {{ background: #b71c1c !important; color: white !important; }}
720
+ .importance-badge.Important {{ background: #bf360c !important; color: white !important; }}
721
+ .importance-badge.Normal {{ background: #2e7d32 !important; color: white !important; }}
722
+ .detection-badge {{ display: inline-block !important; padding: 2px 6px !important; border-radius: 8px !important; font-size: 11px !important; background: #1976d2 !important; color: white !important; margin-left: 5px !important; }}
723
+ .video-links {{ margin-top: 8px !important; }}
724
+ .video-links a {{ display: inline-block !important; margin-right: 15px !important; color: #90caf9 !important; text-decoration: none !important; font-size: 14px !important; padding: 4px 8px !important; border: 1px solid #263238 !important; border-radius: 4px !important; transition: background-color 0.3s !important; }}
725
+ .video-links a:hover {{ background-color: rgba(227,242,253,0.04) !important; }}
726
+ .no-videos {{ text-align: center !important; padding: 40px !important; color: #bdbdbd !important; font-size: 16px !important; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
  </style>
728
  </head>
729
  <body>
 
819
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
820
  <title>YouTube Competitor Analysis Dashboard (Global)</title>
821
  <style>
822
+ body {{ font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #000000 !important; color: #ffffff !important; }}
823
+ .container {{ max-width: 1200px; margin: 0 auto; background-color: transparent !important; }}
824
+ h1 {{ color: #ffffff !important; text-align: center; margin-bottom: 30px; }}
825
+ h2 {{ border-bottom: 2px solid rgba(255,255,255,0.06) !important; padding-bottom: 10px !important; }}
826
+ .section {{ background: #070707 !important; margin-bottom: 30px !important; padding: 20px !important; border-radius: 8px !important; box-shadow: 0 2px 6px rgba(0,0,0,0.6) !important; }}
827
+ .trend-item {{ border: 1px solid #1f1f1f !important; margin-bottom: 15px !important; padding: 15px !important; border-radius: 5px !important; background: #0b0b0b !important; }}
828
+ .trend-title {{ font-size: 18px !important; font-weight: bold !important; color: #ff8a80 !important; margin-bottom: 10px !important; }}
829
+ .trend-meta {{ font-size: 14px !important; color: #bdbdbd !important; margin-bottom: 5px !important; }}
830
+ .video-item {{ display: flex !important; align-items: flex-start !important; margin-bottom: 15px !important; padding: 10px !important; border-left: 4px solid #222 !important; }}
831
+ .video-item.Critical {{ border-left-color: #b71c1c !important; background-color: #120202 !important; }}
832
+ .video-item.Important {{ border-left-color: #bf360c !important; background-color: #241100 !important; }}
833
+ .thumbnail {{ width: 160px !important; height: 90px !important; object-fit: cover !important; margin-right: 15px !important; border-radius: 4px !important; flex-shrink: 0 !important; }}
834
+ .video-info {{ flex: 1 !important; }}
835
+ .video-title {{ font-weight: bold !important; margin-bottom: 5px !important; font-size: 16px !important; color: #ffffff !important; }}
836
+ .video-meta {{ color: #cfcfcf !important; font-size: 14px !important; line-height: 1.6 !important; }}
837
+ .importance-badge {{ display: inline-block !important; padding: 2px 8px !important; border-radius: 12px !important; font-size: 12px !important; font-weight: bold !important; margin-left: 10px !important; }}
838
+ .importance-badge.Critical {{ background: #b71c1c !important; color: white !important; }}
839
+ .importance-badge.Important {{ background: #bf360c !important; color: white !important; }}
840
+ .importance-badge.Normal {{ background: #2e7d32 !important; color: white !important; }}
841
+ .detection-badge {{ display: inline-block !important; padding: 2px 6px !important; border-radius: 8px !important; font-size: 11px !important; background: #1976d2 !important; color: white !important; margin-left: 5px !important; }}
842
+ .stats {{ color: #64b5f6 !important; font-weight: bold !important; }}
843
+ .video-links {{ margin-top: 8px !important; }}
844
+ .video-links a {{ display: inline-block !important; margin-right: 15px !important; color: #90caf9 !important; text-decoration: none !important; font-size: 14px !important; padding: 4px 8px !important; border: 1px solid #263238 !important; border-radius: 4px !important; transition: background-color 0.3s !important; }}
845
+ .video-links a:hover {{ background-color: rgba(227,242,253,0.04) !important; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  </style>
847
  </head>
848
  <body>
 
927
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
928
  <title>Channel Management (Global)</title>
929
  <style>
930
+ body {{ font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0; padding: 20px; background-color: #000000 !important; color: #ffffff !important; }}
931
+ .container {{ max-width: 800px; margin: 0 auto; background-color: transparent !important; }}
932
+ h2 {{ border-bottom: 2px solid rgba(255,255,255,0.06) !important; padding-bottom: 10px !important; }}
933
+ .channel-item {{ display: flex !important; align-items: center !important; background: #0b0b0b !important; margin-bottom: 15px !important; padding: 15px !important; border-radius: 8px !important; box-shadow: 0 2px 6px rgba(0,0,0,0.6) !important; }}
934
+ .channel-icon {{ width: 48px !important; height: 48px !important; border-radius: 50% !important; margin-right: 15px !important; object-fit: cover !important; }}
935
+ .channel-info {{ flex: 1 !important; }}
936
+ .channel-name {{ font-weight: bold !important; font-size: 16px !important; margin-bottom: 5px !important; color: #ffffff !important; }}
937
+ .channel-meta {{ color: #bdbdbd !important; font-size: 14px !important; }}
938
+ .channel-actions {{ display: flex !important; gap: 10px !important; }}
939
+ .btn {{ padding: 6px 12px !important; border: none !important; border-radius: 4px !important; cursor: pointer !important; font-size: 12px !important; text-decoration: none !important; display: inline-block !important; transition: opacity 0.3s !important; }}
940
+ .btn-edit {{ background: #1976d2 !important; color: white !important; border: 1px solid #263238 !important; }}
941
+ .btn-delete {{ background: #b71c1c !important; color: white !important; border: 1px solid #331111 !important; }}
942
+ .btn:hover {{ opacity: 0.9 !important; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
943
  </style>
944
  </head>
945
  <body>
 
998
  # アプリのインスタンスを作成
999
  analyzer = YouTubeCompetitorAnalyzer()
1000
 
1001
+ # Strong global dark CSS to force dark mode even if hosting injects light styles.
1002
+ # Uses high-specificity selectors and !important to override Hugging Face Spaces' theme.
1003
+ DARK_THEME_CSS = """
1004
+ :root, html, body, .gradio-container {
1005
+ background-color: #000000 !important;
1006
+ color: #ffffff !important;
1007
+ color-scheme: dark !important;
1008
+ }
1009
+ .gradio-container, .gradio-container * {
1010
+ background-color: transparent !important;
1011
+ color: #ffffff !important;
1012
+ border-color: #333333 !important;
1013
+ }
1014
+ /* Inputs, buttons and textareas */
1015
+ button, .gr-button, input, textarea, select, .gradio-textbox, .gradio-file, .gradio-dropdown, .gradio-button {
1016
+ background-color: #0b0b0b !important;
1017
+ color: #ffffff !important;
1018
+ border: 1px solid #333333 !important;
1019
+ }
1020
+ input::placeholder, textarea::placeholder {
1021
+ color: #bfbfbf !important;
1022
+ }
1023
+ .gradio-markdown, .gradio-html, .gradio-label, .gradio-textbox, .gradio-output {
1024
+ color: #ffffff !important;
1025
+ }
1026
+ /* Ensure components that Gradio or Spaces might wrap still show dark backgrounds */
1027
+ .gradio-container .container, .gradio-container .section, .gradio-container .card {
1028
+ background-color: #000000 !important;
1029
+ color: #ffffff !important;
1030
+ }
1031
+ /* Give high contrast to borders and badges */
1032
+ .importance-badge, .detection-badge {
1033
+ color: #ffffff !important;
1034
+ }
1035
+ """
1036
+
1037
  # Gradio インターface
1038
  def add_channel_interface(channel_ids_text):
1039
  """Interface function that supports adding multiple channel IDs"""
 
1096
  return analyzer.generate_recent_videos_html(hours, limit)
1097
 
1098
  # Gradioアプリの構築
1099
+ with gr.Blocks(title="YouTube Competitor Analysis (Global)", theme=gr.themes.Dark(), css=DARK_THEME_CSS) as app:
1100
  gr.Markdown("# 🌍 YouTube Competitor Analysis App (Global)")
1101
  gr.Markdown("Analyze competitor channel uploads and detect global clustered trends using **Gemini 2.5 Flash**.")
1102