seawolf2357 commited on
Commit
616e960
ยท
verified ยท
1 Parent(s): b1a8e9d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +374 -42
app.py CHANGED
@@ -2,24 +2,26 @@ from flask import Flask, render_template, request, jsonify
2
  import requests
3
  import os
4
  import time
 
 
5
 
6
  app = Flask(__name__)
7
 
8
  # Function to fetch trending spaces from Huggingface with pagination
9
  def fetch_trending_spaces(offset=0, limit=72):
10
  try:
11
- # ๋‹จ์ˆœํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
12
  url = "https://huggingface.co/api/spaces"
13
- params = {"limit": 500} # ์ตœ๋Œ€ 500๊ฐœ ๊ฐ€์ ธ์˜ค๊ธฐ
14
 
15
- # ํƒ€์ž„์•„์›ƒ ๋Š˜๋ฆฌ๊ธฐ
16
  response = requests.get(url, params=params, timeout=30)
17
 
18
  if response.status_code == 200:
19
  spaces = response.json()
20
  filtered_spaces = [space for space in spaces if space.get('owner') != 'None' and space.get('id', '').split('/', 1)[0] != 'None']
21
 
22
- # ์š”์ฒญ๋œ offset๊ณผ limit์— ๋งž๊ฒŒ ์Šฌ๋ผ์ด์‹ฑ
23
  start = min(offset, len(filtered_spaces))
24
  end = min(offset + limit, len(filtered_spaces))
25
 
@@ -29,28 +31,31 @@ def fetch_trending_spaces(offset=0, limit=72):
29
  'spaces': filtered_spaces[start:end],
30
  'total': len(filtered_spaces),
31
  'offset': offset,
32
- 'limit': limit
 
33
  }
34
  else:
35
  print(f"Error fetching spaces: {response.status_code}")
36
- # ๋นˆ ๊ณต๊ฐ„ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ 200๊ฐœ ์ œํ•œ์˜ ๊ฐ€์งœ ๋ฐ์ดํ„ฐ
37
  return {
38
  'spaces': generate_dummy_spaces(limit),
39
  'total': 200,
40
  'offset': offset,
41
- 'limit': limit
 
42
  }
43
  except Exception as e:
44
  print(f"Exception when fetching spaces: {e}")
45
- # ๊ฐ€์งœ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
46
  return {
47
  'spaces': generate_dummy_spaces(limit),
48
  'total': 200,
49
  'offset': offset,
50
- 'limit': limit
 
51
  }
52
 
53
- # ์˜ค๋ฅ˜ ์‹œ ๊ฐ€์งœ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
54
  def generate_dummy_spaces(count):
55
  spaces = []
56
  for i in range(count):
@@ -65,11 +70,11 @@ def generate_dummy_spaces(count):
65
 
66
  # Transform Huggingface URL to direct space URL
67
  def transform_url(owner, name):
68
- # 1. '.' ๋ฌธ์ž๋ฅผ '-'๋กœ ๋ณ€๊ฒฝ
69
  name = name.replace('.', '-')
70
- # 2. '_' ๋ฌธ์ž๋ฅผ '-'๋กœ ๋ณ€๊ฒฝ
71
  name = name.replace('_', '-')
72
- # 3. ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด ๋ชจ๋‘ ์†Œ๋ฌธ์ž๋กœ ๋ณ€๊ฒฝ
73
  owner = owner.lower()
74
  name = name.lower()
75
 
@@ -78,28 +83,28 @@ def transform_url(owner, name):
78
  # Get space details
79
  def get_space_details(space_data, index, offset):
80
  try:
81
- # ๊ณตํ†ต ์ •๋ณด ์ถ”์ถœ
82
  if '/' in space_data.get('id', ''):
83
  owner, name = space_data.get('id', '').split('/', 1)
84
  else:
85
  owner = space_data.get('owner', '')
86
  name = space_data.get('id', '')
87
 
88
- # None์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ ๋ฌด์‹œ
89
  if owner == 'None' or name == 'None':
90
  return None
91
 
92
- # URL ๊ตฌ์„ฑ
93
  original_url = f"https://huggingface.co/spaces/{owner}/{name}"
94
  embed_url = transform_url(owner, name)
95
 
96
- # ์ข‹์•„์š” ์ˆ˜
97
  likes_count = space_data.get('likes', 0)
98
 
99
- # ์ œ๋ชฉ ์ถ”์ถœ
100
  title = space_data.get('title', name)
101
 
102
- # ํƒœ๊ทธ
103
  tags = space_data.get('tags', [])
104
 
105
  return {
@@ -107,14 +112,14 @@ def get_space_details(space_data, index, offset):
107
  'embedUrl': embed_url,
108
  'title': title,
109
  'owner': owner,
110
- 'name': name, # Space ์ด๋ฆ„ ์ถ”๊ฐ€ ์ €์žฅ
111
  'likes_count': likes_count,
112
  'tags': tags,
113
  'rank': offset + index + 1
114
  }
115
  except Exception as e:
116
  print(f"Error processing space data: {e}")
117
- # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ์—๋„ ๊ธฐ๋ณธ ๊ฐ์ฒด ๋ฐ˜ํ™˜
118
  return {
119
  'url': 'https://huggingface.co/spaces',
120
  'embedUrl': 'https://huggingface.co/spaces',
@@ -126,6 +131,26 @@ def get_space_details(space_data, index, offset):
126
  'rank': offset + index + 1
127
  }
128
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  # Homepage route
130
  @app.route('/')
131
  def home():
@@ -136,7 +161,7 @@ def home():
136
  def trending_spaces():
137
  search_query = request.args.get('search', '').lower()
138
  offset = int(request.args.get('offset', 0))
139
- limit = int(request.args.get('limit', 72)) # ๊ธฐ๋ณธ๊ฐ’ 72๊ฐœ๋กœ ๋ณ€๊ฒฝ
140
 
141
  # Fetch trending spaces
142
  spaces_data = fetch_trending_spaces(offset, limit)
@@ -164,11 +189,15 @@ def trending_spaces():
164
 
165
  results.append(space_info)
166
 
 
 
 
167
  return jsonify({
168
  'spaces': results,
169
  'total': spaces_data['total'],
170
  'offset': offset,
171
- 'limit': limit
 
172
  })
173
 
174
  if __name__ == '__main__':
@@ -184,6 +213,7 @@ if __name__ == '__main__':
184
  <meta charset="UTF-8">
185
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
186
  <title>Huggingface Spaces Gallery</title>
 
187
  <style>
188
  @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap');
189
 
@@ -519,10 +549,19 @@ if __name__ == '__main__':
519
  text-align: center;
520
  }
521
 
522
- .error-icon {
523
- font-size: 3rem;
524
  margin-bottom: 1rem;
525
- color: #e53e3e;
 
 
 
 
 
 
 
 
 
526
  }
527
 
528
  /* Pagination Styling */
@@ -606,6 +645,58 @@ if __name__ == '__main__':
606
  font-size: 0.9rem;
607
  }
608
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
  /* Responsive Design */
610
  @media (max-width: 768px) {
611
  body {
@@ -634,6 +725,10 @@ if __name__ == '__main__':
634
  .pagination {
635
  flex-wrap: wrap;
636
  }
 
 
 
 
637
  }
638
  </style>
639
  </head>
@@ -655,6 +750,29 @@ if __name__ == '__main__':
655
  <p>Discover the top 500 trending spaces from the Huggingface</p>
656
  </div>
657
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  <div class="search-bar">
659
  <input type="text" id="searchInput" placeholder="Search by name, owner, or tags..." />
660
  <button id="refreshButton" class="refresh-btn">
@@ -690,7 +808,10 @@ if __name__ == '__main__':
690
  loadingError: document.getElementById('loadingError'),
691
  searchInput: document.getElementById('searchInput'),
692
  refreshButton: document.getElementById('refreshButton'),
693
- pagination: document.getElementById('pagination')
 
 
 
694
  };
695
 
696
  // Application state
@@ -701,9 +822,122 @@ if __name__ == '__main__':
701
  itemsPerPage: 72, // 72 items per page
702
  totalItems: 0,
703
  loadingTimeout: null,
704
- staticModeAttempted: {} // Track which spaces have attempted static mode
 
 
 
705
  };
706
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  // Display loading indicator
708
  function setLoading(isLoading) {
709
  state.isLoading = isLoading;
@@ -750,6 +984,92 @@ if __name__ == '__main__':
750
  }
751
  }
752
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
  // Load spaces with timeout
754
  async function loadSpaces(page = 0) {
755
  setLoading(true);
@@ -773,9 +1093,15 @@ if __name__ == '__main__':
773
  state.spaces = data.spaces;
774
  state.totalItems = data.total;
775
  state.currentPage = page;
 
776
 
777
  renderGrid(data.spaces);
778
  renderPagination();
 
 
 
 
 
779
  } catch (error) {
780
  console.error('Error loading spaces:', error);
781
 
@@ -854,7 +1180,7 @@ if __name__ == '__main__':
854
  elements.pagination.appendChild(nextButton);
855
  }
856
 
857
- // Handle iframe error and provide static site fallback
858
  function handleIframeError(iframe, owner, name, title) {
859
  const container = iframe.parentNode;
860
 
@@ -862,11 +1188,11 @@ if __name__ == '__main__':
862
  const errorPlaceholder = document.createElement('div');
863
  errorPlaceholder.className = 'error-placeholder';
864
 
865
- // Error icon
866
- const errorIcon = document.createElement('div');
867
- errorIcon.className = 'error-icon';
868
- errorIcon.textContent = 'โš ๏ธ';
869
- errorPlaceholder.appendChild(errorIcon);
870
 
871
  // Error message
872
  const errorMessage = document.createElement('p');
@@ -1008,7 +1334,7 @@ if __name__ == '__main__':
1008
  state.staticModeAttempted[spaceKey] = true;
1009
  iframe.src = directUrl + '/index.html';
1010
  } else {
1011
- // If static mode also failed, show error
1012
  handleIframeError(iframe, owner, name, title);
1013
  }
1014
  };
@@ -1023,13 +1349,16 @@ if __name__ == '__main__':
1023
  iframeDoc.body.textContent.includes('404') ||
1024
  iframeDoc.body.textContent.includes('not found');
1025
 
1026
- if (isErrorPage && !state.staticModeAttempted[spaceKey]) {
1027
- // If it's an error page and we haven't tried static mode yet
1028
- state.staticModeAttempted[spaceKey] = true;
1029
- iframe.src = directUrl + '/index.html';
1030
- } else if (isErrorPage) {
1031
- // If static mode already attempted and still failing
1032
- handleIframeError(iframe, owner, name, title);
 
 
 
1033
  }
1034
  } catch (e) {
1035
  // Cross-origin errors are expected, this generally means the iframe loaded
@@ -1092,6 +1421,9 @@ if __name__ == '__main__':
1092
  // Refresh button event listener
1093
  elements.refreshButton.addEventListener('click', () => loadSpaces(0));
1094
 
 
 
 
1095
  // Mac buttons functionality (just for show)
1096
  document.querySelectorAll('.mac-button').forEach(button => {
1097
  button.addEventListener('click', function(e) {
 
2
  import requests
3
  import os
4
  import time
5
+ import random
6
+ from collections import Counter
7
 
8
  app = Flask(__name__)
9
 
10
  # Function to fetch trending spaces from Huggingface with pagination
11
  def fetch_trending_spaces(offset=0, limit=72):
12
  try:
13
+ # Simple data fetching
14
  url = "https://huggingface.co/api/spaces"
15
+ params = {"limit": 500} # Get max 500
16
 
17
+ # Increase timeout
18
  response = requests.get(url, params=params, timeout=30)
19
 
20
  if response.status_code == 200:
21
  spaces = response.json()
22
  filtered_spaces = [space for space in spaces if space.get('owner') != 'None' and space.get('id', '').split('/', 1)[0] != 'None']
23
 
24
+ # Slice according to requested offset and limit
25
  start = min(offset, len(filtered_spaces))
26
  end = min(offset + limit, len(filtered_spaces))
27
 
 
31
  'spaces': filtered_spaces[start:end],
32
  'total': len(filtered_spaces),
33
  'offset': offset,
34
+ 'limit': limit,
35
+ 'all_spaces': filtered_spaces # Return all spaces for stats calculation
36
  }
37
  else:
38
  print(f"Error fetching spaces: {response.status_code}")
39
+ # Return empty spaces with fake 200 limit data
40
  return {
41
  'spaces': generate_dummy_spaces(limit),
42
  'total': 200,
43
  'offset': offset,
44
+ 'limit': limit,
45
+ 'all_spaces': generate_dummy_spaces(500) # Dummy data for stats
46
  }
47
  except Exception as e:
48
  print(f"Exception when fetching spaces: {e}")
49
+ # Generate fake data
50
  return {
51
  'spaces': generate_dummy_spaces(limit),
52
  'total': 200,
53
  'offset': offset,
54
+ 'limit': limit,
55
+ 'all_spaces': generate_dummy_spaces(500) # Dummy data for stats
56
  }
57
 
58
+ # Generate dummy spaces in case of error
59
  def generate_dummy_spaces(count):
60
  spaces = []
61
  for i in range(count):
 
70
 
71
  # Transform Huggingface URL to direct space URL
72
  def transform_url(owner, name):
73
+ # 1. Replace '.' with '-'
74
  name = name.replace('.', '-')
75
+ # 2. Replace '_' with '-'
76
  name = name.replace('_', '-')
77
+ # 3. Convert to lowercase
78
  owner = owner.lower()
79
  name = name.lower()
80
 
 
83
  # Get space details
84
  def get_space_details(space_data, index, offset):
85
  try:
86
+ # Extract common info
87
  if '/' in space_data.get('id', ''):
88
  owner, name = space_data.get('id', '').split('/', 1)
89
  else:
90
  owner = space_data.get('owner', '')
91
  name = space_data.get('id', '')
92
 
93
+ # Ignore if contains None
94
  if owner == 'None' or name == 'None':
95
  return None
96
 
97
+ # Construct URLs
98
  original_url = f"https://huggingface.co/spaces/{owner}/{name}"
99
  embed_url = transform_url(owner, name)
100
 
101
+ # Likes count
102
  likes_count = space_data.get('likes', 0)
103
 
104
+ # Extract title
105
  title = space_data.get('title', name)
106
 
107
+ # Tags
108
  tags = space_data.get('tags', [])
109
 
110
  return {
 
112
  'embedUrl': embed_url,
113
  'title': title,
114
  'owner': owner,
115
+ 'name': name, # Store Space name
116
  'likes_count': likes_count,
117
  'tags': tags,
118
  'rank': offset + index + 1
119
  }
120
  except Exception as e:
121
  print(f"Error processing space data: {e}")
122
+ # Return basic object even if error occurs
123
  return {
124
  'url': 'https://huggingface.co/spaces',
125
  'embedUrl': 'https://huggingface.co/spaces',
 
131
  'rank': offset + index + 1
132
  }
133
 
134
+ # Get owner statistics from all spaces
135
+ def get_owner_stats(all_spaces):
136
+ owners = []
137
+ for space in all_spaces:
138
+ if '/' in space.get('id', ''):
139
+ owner, _ = space.get('id', '').split('/', 1)
140
+ else:
141
+ owner = space.get('owner', '')
142
+
143
+ if owner != 'None':
144
+ owners.append(owner)
145
+
146
+ # Count occurrences of each owner
147
+ owner_counts = Counter(owners)
148
+
149
+ # Get top 30 owners by count
150
+ top_owners = owner_counts.most_common(30)
151
+
152
+ return top_owners
153
+
154
  # Homepage route
155
  @app.route('/')
156
  def home():
 
161
  def trending_spaces():
162
  search_query = request.args.get('search', '').lower()
163
  offset = int(request.args.get('offset', 0))
164
+ limit = int(request.args.get('limit', 72)) # Default 72
165
 
166
  # Fetch trending spaces
167
  spaces_data = fetch_trending_spaces(offset, limit)
 
189
 
190
  results.append(space_info)
191
 
192
+ # Get owner statistics for all spaces
193
+ top_owners = get_owner_stats(spaces_data.get('all_spaces', []))
194
+
195
  return jsonify({
196
  'spaces': results,
197
  'total': spaces_data['total'],
198
  'offset': offset,
199
+ 'limit': limit,
200
+ 'top_owners': top_owners # Add top owners data
201
  })
202
 
203
  if __name__ == '__main__':
 
213
  <meta charset="UTF-8">
214
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
215
  <title>Huggingface Spaces Gallery</title>
216
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
217
  <style>
218
  @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap');
219
 
 
549
  text-align: center;
550
  }
551
 
552
+ .error-emoji {
553
+ font-size: 5rem;
554
  margin-bottom: 1rem;
555
+ animation: bounce 1s infinite alternate;
556
+ }
557
+
558
+ @keyframes bounce {
559
+ from {
560
+ transform: translateY(0px);
561
+ }
562
+ to {
563
+ transform: translateY(-10px);
564
+ }
565
  }
566
 
567
  /* Pagination Styling */
 
645
  font-size: 0.9rem;
646
  }
647
 
648
+ /* Stats window styling */
649
+ .stats-window {
650
+ margin-top: 2rem;
651
+ margin-bottom: 2rem;
652
+ }
653
+
654
+ .stats-header {
655
+ display: flex;
656
+ justify-content: space-between;
657
+ align-items: center;
658
+ margin-bottom: 1rem;
659
+ }
660
+
661
+ .stats-title {
662
+ font-size: 1.5rem;
663
+ font-weight: 700;
664
+ color: #2d3748;
665
+ }
666
+
667
+ .stats-toggle {
668
+ background-color: var(--pastel-blue);
669
+ border: none;
670
+ padding: 8px 16px;
671
+ border-radius: 20px;
672
+ font-weight: 600;
673
+ cursor: pointer;
674
+ transition: all 0.2s;
675
+ }
676
+
677
+ .stats-toggle:hover {
678
+ background-color: var(--pastel-purple);
679
+ }
680
+
681
+ .stats-content {
682
+ background-color: white;
683
+ border-radius: 10px;
684
+ padding: 20px;
685
+ box-shadow: var(--box-shadow);
686
+ max-height: 0;
687
+ overflow: hidden;
688
+ transition: max-height 0.5s ease-out;
689
+ }
690
+
691
+ .stats-content.open {
692
+ max-height: 600px;
693
+ }
694
+
695
+ .chart-container {
696
+ width: 100%;
697
+ height: 500px;
698
+ }
699
+
700
  /* Responsive Design */
701
  @media (max-width: 768px) {
702
  body {
 
725
  .pagination {
726
  flex-wrap: wrap;
727
  }
728
+
729
+ .chart-container {
730
+ height: 300px;
731
+ }
732
  }
733
  </style>
734
  </head>
 
750
  <p>Discover the top 500 trending spaces from the Huggingface</p>
751
  </div>
752
 
753
+ <!-- Stats Section -->
754
+ <div class="stats-window mac-window">
755
+ <div class="mac-toolbar">
756
+ <div class="mac-buttons">
757
+ <div class="mac-button mac-close"></div>
758
+ <div class="mac-button mac-minimize"></div>
759
+ <div class="mac-button mac-maximize"></div>
760
+ </div>
761
+ <div class="mac-title">Creator Statistics</div>
762
+ </div>
763
+ <div class="mac-content">
764
+ <div class="stats-header">
765
+ <div class="stats-title">Top 30 Creators by Number of Spaces</div>
766
+ <button id="statsToggle" class="stats-toggle">Show Stats</button>
767
+ </div>
768
+ <div id="statsContent" class="stats-content">
769
+ <div class="chart-container">
770
+ <canvas id="creatorStatsChart"></canvas>
771
+ </div>
772
+ </div>
773
+ </div>
774
+ </div>
775
+
776
  <div class="search-bar">
777
  <input type="text" id="searchInput" placeholder="Search by name, owner, or tags..." />
778
  <button id="refreshButton" class="refresh-btn">
 
808
  loadingError: document.getElementById('loadingError'),
809
  searchInput: document.getElementById('searchInput'),
810
  refreshButton: document.getElementById('refreshButton'),
811
+ pagination: document.getElementById('pagination'),
812
+ statsToggle: document.getElementById('statsToggle'),
813
+ statsContent: document.getElementById('statsContent'),
814
+ creatorStatsChart: document.getElementById('creatorStatsChart')
815
  };
816
 
817
  // Application state
 
822
  itemsPerPage: 72, // 72 items per page
823
  totalItems: 0,
824
  loadingTimeout: null,
825
+ staticModeAttempted: {}, // Track which spaces have attempted static mode
826
+ statsVisible: false,
827
+ chartInstance: null,
828
+ topOwners: []
829
  };
830
 
831
+ // Random emoji list for 404 errors
832
+ const randomEmojis = [
833
+ '๐Ÿ™ˆ', '๐Ÿ™‰', '๐Ÿ™Š', '๐Ÿต', '๐Ÿ’', '๐Ÿฆ', '๐Ÿฆง', '๐Ÿถ', '๐Ÿ•', '๐Ÿฆฎ',
834
+ '๐Ÿฉ', '๐Ÿบ', '๐ŸฆŠ', '๐Ÿฆ', '๐Ÿฑ', '๐Ÿˆ', '๐Ÿฆ', '๐Ÿฏ', '๐Ÿ…', '๐Ÿ†',
835
+ '๐Ÿด', '๐ŸŽ', '๐Ÿฆ„', '๐Ÿฆ“', '๐ŸฆŒ', '๐Ÿฎ', '๐Ÿ‚', '๐Ÿƒ', '๐Ÿ„', '๐Ÿท',
836
+ '๐Ÿ–', '๐Ÿ—', '๐Ÿ', '๐Ÿ‘', '๐Ÿ', '๐Ÿช', '๐Ÿซ', '๐Ÿฆ™', '๐Ÿฆ’', '๐Ÿ˜',
837
+ '๐Ÿฆ', '๐Ÿฆ›', '๐Ÿญ', '๐Ÿ', '๐Ÿ€', '๐Ÿน', '๐Ÿฐ', '๐Ÿ‡', '๐Ÿฟ๏ธ', '๐Ÿฆ”',
838
+ '๐Ÿฆ‡', '๐Ÿป', '๐Ÿจ', '๐Ÿผ', '๐Ÿฆฅ', '๐Ÿฆฆ', '๐Ÿฆจ', '๐Ÿฆ˜', '๐Ÿฆก', '๐Ÿพ',
839
+ '๐Ÿฆƒ', '๐Ÿ”', '๐Ÿ“', '๐Ÿฃ', '๐Ÿค', '๐Ÿฅ', '๐Ÿฆ', '๐Ÿง', '๐Ÿ•Š๏ธ', '๐Ÿฆ…',
840
+ '๐Ÿฆ†', '๐Ÿฆข', '๐Ÿฆ‰', '๐Ÿฆฉ', '๐Ÿฆš', '๐Ÿฆœ', '๐Ÿธ', '๐ŸŠ', '๐Ÿข', '๐ŸฆŽ',
841
+ '๐Ÿ', '๐Ÿฒ', '๐Ÿ‰', '๐Ÿฆ•', '๐Ÿฆ–', '๐Ÿณ', '๐Ÿ‹', '๐Ÿฌ', '๐ŸŸ', '๐Ÿ ',
842
+ '๐Ÿก', '๐Ÿฆˆ', '๐Ÿ™', '๐Ÿš', '๐ŸŒ', '๐Ÿฆ‹', '๐Ÿ›', '๐Ÿœ', '๐Ÿ', '๐Ÿž',
843
+ '๐Ÿฆ—', '๐Ÿ•ท๏ธ', '๐Ÿ•ธ๏ธ', '๐Ÿฆ‚', '๐ŸฆŸ', '๐Ÿฆ ', '๐Ÿ’', '๐ŸŒธ', '๐Ÿ’ฎ', '๐Ÿต๏ธ',
844
+ '๐ŸŒน', '๐Ÿฅ€', '๐ŸŒบ', '๐ŸŒป', '๐ŸŒผ', '๐ŸŒท', '๐ŸŒฑ', '๐ŸŒฒ', '๐ŸŒณ', '๐ŸŒด',
845
+ '๐ŸŒต', '๐ŸŒพ', '๐ŸŒฟ', 'โ˜˜๏ธ', '๐Ÿ€', '๐Ÿ', '๐Ÿ‚', '๐Ÿƒ', '๐Ÿ‡', '๐Ÿˆ',
846
+ '๐Ÿ‰', '๐ŸŠ', '๐Ÿ‹', '๐ŸŒ', '๐Ÿ', '๐Ÿฅญ', '๐ŸŽ', '๐Ÿ', '๐Ÿ', '๐Ÿ‘',
847
+ '๐Ÿ’', '๐Ÿ“', '๐Ÿฅ', '๐Ÿ…', '๐Ÿฅฅ', '๐Ÿฅ‘', '๐Ÿ†', '๐Ÿฅ”', '๐Ÿฅ•', '๐ŸŒฝ',
848
+ '๐ŸŒถ๏ธ', '๐Ÿฅ’', '๐Ÿฅฌ', '๐Ÿฅฆ', '๐Ÿง„', '๐Ÿง…', '๐Ÿ„', '๐Ÿฅœ', '๐ŸŒฐ', '๐Ÿž',
849
+ '๐Ÿฅ', '๐Ÿฅ–', '๐Ÿฅจ', '๐Ÿฅฏ', '๐Ÿฅž', '๐Ÿง‡', '๐Ÿง€', '๐Ÿ–', '๐Ÿ—', '๐Ÿฅฉ',
850
+ '๐Ÿฅ“', '๐Ÿ”', '๐ŸŸ', '๐Ÿ•', '๐ŸŒญ', '๐Ÿฅช', '๐ŸŒฎ', '๐ŸŒฏ', '๐Ÿฅ™', '๐Ÿง†',
851
+ '๐Ÿฅš', '๐Ÿณ', '๐Ÿฅ˜', '๐Ÿฒ', '๐Ÿฅฃ', '๐Ÿฅ—', '๐Ÿฟ', '๐Ÿงˆ', '๐Ÿง‚', '๐Ÿฅซ',
852
+ '๐Ÿฑ', '๐Ÿ˜', '๐Ÿ™', '๐Ÿš', '๐Ÿ›', '๐Ÿœ', '๐Ÿ', '๐Ÿ ', '๐Ÿข', '๐Ÿฃ',
853
+ '๐Ÿค', '๐Ÿฅ', '๐Ÿฅฎ', '๐Ÿก', '๐ŸฅŸ', '๐Ÿฅ ', '๐Ÿฅก', '๐Ÿฆ€', '๐Ÿฆž', '๐Ÿฆ',
854
+ '๐Ÿฆ‘', '๐Ÿฆช', '๐Ÿฆ', '๐Ÿง', '๐Ÿจ', '๐Ÿฉ', '๐Ÿช', '๐ŸŽ‚', '๐Ÿฐ', '๐Ÿง',
855
+ '๐Ÿฅง', '๐Ÿซ', '๐Ÿฌ', '๐Ÿญ', '๐Ÿฎ', '๐Ÿฏ', '๐Ÿผ', '๐Ÿฅ›', 'โ˜•', '๐Ÿต',
856
+ '๐Ÿถ', '๐Ÿพ', '๐Ÿท', '๐Ÿธ', '๐Ÿน', '๐Ÿบ', '๐Ÿป', '๐Ÿฅ‚', '๐Ÿฅƒ', '๐Ÿฅค',
857
+ '๐Ÿงƒ', '๐Ÿง‰', '๐ŸงŠ', '๐Ÿฅข', '๐Ÿฝ๏ธ', '๐Ÿด', '๐Ÿฅ„', '๐Ÿ”ช', '๐Ÿบ', '๐ŸŒ',
858
+ '๐ŸŒŽ', '๐ŸŒ', '๐ŸŒ', '๐Ÿ—บ๏ธ', '๐Ÿ—พ', '๐Ÿงญ', '๐Ÿ”๏ธ', 'โ›ฐ๏ธ', '๐ŸŒ‹', '๐Ÿ—ป',
859
+ '๐Ÿ•๏ธ', '๐Ÿ–๏ธ', '๐Ÿœ๏ธ', '๐Ÿ๏ธ', '๐Ÿž๏ธ', '๐ŸŸ๏ธ', '๐Ÿ›๏ธ', '๐Ÿ—๏ธ', '๐Ÿงฑ', '๐Ÿ˜๏ธ',
860
+ '๐Ÿš๏ธ', '๐Ÿ ', '๐Ÿก', '๐Ÿข', '๐Ÿฃ', '๐Ÿค', '๐Ÿฅ', '๐Ÿฆ', '๐Ÿจ', '๐Ÿฉ',
861
+ '๐Ÿช', '๐Ÿซ', '๐Ÿฌ', '๐Ÿญ', '๐Ÿฏ', '๐Ÿฐ', '๐Ÿ’’', '๐Ÿ—ผ', '๐Ÿ—ฝ', 'โ›ช',
862
+ '๐Ÿ•Œ', '๐Ÿ›•', '๐Ÿ•', 'โ›ฉ๏ธ', '๐Ÿ•‹', 'โ›ฒ', 'โ›บ', '๐ŸŒ', '๐ŸŒƒ', '๐Ÿ™๏ธ',
863
+ '๐ŸŒ„', '๐ŸŒ…', '๐ŸŒ†', '๐ŸŒ‡', '๐ŸŒ‰', 'โ™จ๏ธ', '๐ŸŽ ', '๐ŸŽก', '๐ŸŽข', '๐Ÿ’ˆ',
864
+ '๐ŸŽช', '๐Ÿš‚', '๐Ÿšƒ', '๐Ÿš„', '๐Ÿš…', '๐Ÿš†', '๐Ÿš‡', '๐Ÿšˆ', '๐Ÿš‰', '๐ŸšŠ',
865
+ '๐Ÿš', '๐Ÿšž', '๐Ÿš‹', '๐ŸšŒ', '๐Ÿš', '๐ŸšŽ', '๐Ÿš', '๐Ÿš‘', '๐Ÿš’', '๐Ÿš“',
866
+ '๐Ÿš”', '๐Ÿš•', '๐Ÿš–', '๐Ÿš—', '๐Ÿš˜', '๐Ÿš™', '๐Ÿšš', '๐Ÿš›', '๐Ÿšœ', '๐ŸŽ๏ธ',
867
+ '๐Ÿ๏ธ', '๐Ÿ›ต', '๐Ÿฆฝ', '๐Ÿฆผ', '๐Ÿ›บ', '๐Ÿšฒ', '๐Ÿ›ด', '๐Ÿ›น', '๐Ÿš', '๐Ÿ›ฃ๏ธ',
868
+ '๐Ÿ›ค๏ธ', '๐Ÿ›ข๏ธ', 'โ›ฝ', '๐Ÿšจ', '๐Ÿšฅ', '๐Ÿšฆ', '๐Ÿ›‘', '๐Ÿšง', 'โš“', 'โ›ต',
869
+ '๐Ÿ›ถ', '๐Ÿšค', '๐Ÿ›ณ๏ธ', 'โ›ด๏ธ', '๐Ÿ›ฅ๏ธ', '๐Ÿšข', 'โœˆ๏ธ', '๐Ÿ›ฉ๏ธ', '๐Ÿ›ซ', '๐Ÿ›ฌ',
870
+ '๐Ÿช‚', '๐Ÿ’บ', '๐Ÿš', '๐ŸšŸ', '๐Ÿš ', '๐Ÿšก', '๐Ÿ›ฐ๏ธ', '๐Ÿš€', '๐Ÿ›ธ', '๐Ÿ›Ž๏ธ',
871
+ '๐Ÿงณ', 'โŒ›', 'โณ', 'โŒš', 'โฐ', 'โฑ๏ธ', 'โฒ๏ธ', '๐Ÿ•ฐ๏ธ', '๐Ÿ•›', '๐Ÿ•ง',
872
+ '๐Ÿ•', '๐Ÿ•œ', '๐Ÿ•‘', '๐Ÿ•', '๐Ÿ•’', '๐Ÿ•ž', '๐Ÿ•“', '๐Ÿ•Ÿ', '๐Ÿ•”', '๐Ÿ• ',
873
+ '๐Ÿ••', '๐Ÿ•ก', '๐Ÿ•–', '๐Ÿ•ข', '๐Ÿ•—', '๐Ÿ•ฃ', '๐Ÿ•˜', '๐Ÿ•ค', '๐Ÿ•™', '๐Ÿ•ฅ',
874
+ '๐Ÿ•š', '๐Ÿ•ฆ', '๐ŸŒ‘', '๐ŸŒ’', '๐ŸŒ“', '๐ŸŒ”', '๐ŸŒ•', '๐ŸŒ–', '๐ŸŒ—', '๐ŸŒ˜',
875
+ '๐ŸŒ™', '๐ŸŒš', '๐ŸŒ›', '๐ŸŒœ', '๐ŸŒก๏ธ', 'โ˜€๏ธ', '๐ŸŒ', '๐ŸŒž', '๐Ÿช', 'โญ',
876
+ '๐ŸŒŸ', '๐ŸŒ ', '๐ŸŒŒ', 'โ˜๏ธ', 'โ›…', 'โ›ˆ๏ธ', '๐ŸŒค๏ธ', '๐ŸŒฅ๏ธ', '๐ŸŒฆ๏ธ', '๐ŸŒง๏ธ',
877
+ '๐ŸŒจ๏ธ', '๐ŸŒฉ๏ธ', '๐ŸŒช๏ธ', '๐ŸŒซ๏ธ', '๐ŸŒฌ๏ธ', '๐ŸŒ€', '๐ŸŒˆ', '๐ŸŒ‚', 'โ˜‚๏ธ', 'โ˜”',
878
+ 'โ›ฑ๏ธ', 'โšก', 'โ„๏ธ', 'โ˜ƒ๏ธ', 'โ›„', 'โ˜„๏ธ', '๏ฟฝ๏ฟฝ๏ฟฝ', '๐Ÿ’ง', '๐ŸŒŠ', '๐ŸŽƒ',
879
+ '๐ŸŽ„', '๐ŸŽ†', '๐ŸŽ‡', '๐Ÿงจ', 'โœจ', '๐ŸŽˆ', '๐ŸŽ‰', '๐ŸŽŠ', '๐ŸŽ‹', '๐ŸŽ',
880
+ '๐ŸŽŽ', '๐ŸŽ', '๐ŸŽ', '๐ŸŽ‘', '๐Ÿงง', '๐ŸŽ€', '๐ŸŽ', '๐ŸŽ—๏ธ', '๐ŸŽŸ๏ธ', '๐ŸŽซ',
881
+ '๐ŸŽ–๏ธ', '๐Ÿ†', '๐Ÿ…', '๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', 'โšฝ', 'โšพ', '๐ŸฅŽ', '๐Ÿ€',
882
+ '๐Ÿ', '๐Ÿˆ', '๐Ÿ‰', '๐ŸŽพ', '๐Ÿฅ', '๐ŸŽณ', '๐Ÿ', '๐Ÿ‘', '๐Ÿ’', '๐Ÿฅ',
883
+ '๐Ÿ“', '๐Ÿธ', '๐ŸฅŠ', '๐Ÿฅ‹', '๐Ÿฅ…', 'โ›ณ', 'โ›ธ๏ธ', '๐ŸŽฃ', '๐Ÿคฟ', '๐ŸŽฝ',
884
+ '๐ŸŽฟ', '๐Ÿ›ท', '๐ŸฅŒ', '๐ŸŽฏ', '๐Ÿช€', '๐Ÿช', '๐ŸŽฑ', '๐Ÿ”ฎ', '๐Ÿงฟ', '๐ŸŽฎ',
885
+ '๐Ÿ•น๏ธ', '๐ŸŽฐ', '๐ŸŽฒ', '๐Ÿงฉ', '๐Ÿงธ', 'โ™ ๏ธ', 'โ™ฅ๏ธ', 'โ™ฆ๏ธ', 'โ™ฃ๏ธ', 'โ™Ÿ๏ธ',
886
+ '๐Ÿƒ', '๐Ÿ€„', '๐ŸŽด', '๐ŸŽญ', '๐Ÿ–ผ๏ธ', '๐ŸŽจ', '๐Ÿงต', '๐Ÿงถ', '๐Ÿ‘“', '๐Ÿ•ถ๏ธ',
887
+ '๐Ÿฅฝ', '๐Ÿฅผ', '๐Ÿฆบ', '๐Ÿ‘”', '๐Ÿ‘•', '๐Ÿ‘–', '๐Ÿงฃ', '๐Ÿงค', '๐Ÿงฅ', '๐Ÿงฆ',
888
+ '๐Ÿ‘—', '๐Ÿ‘˜', '๐Ÿฅป', '๐Ÿฉฑ', '๐Ÿฉฒ', '๐Ÿฉณ', '๐Ÿ‘™', '๐Ÿ‘š', '๐Ÿ‘›', '๐Ÿ‘œ',
889
+ '๐Ÿ‘', '๐Ÿ›๏ธ', '๐ŸŽ’', '๐Ÿ‘ž', '๐Ÿ‘Ÿ', '๐Ÿฅพ', '๐Ÿฅฟ', '๐Ÿ‘ ', '๐Ÿ‘ก', '๐Ÿฉฐ',
890
+ '๐Ÿ‘ข', '๐Ÿ‘‘', '๐Ÿ‘’', '๐ŸŽฉ', '๐ŸŽ“', '๐Ÿงข', 'โ›‘๏ธ', '๐Ÿ“ฟ', '๐Ÿ’„', '๐Ÿ’',
891
+ '๐Ÿ’Ž', '๐Ÿ”‡', '๐Ÿ”ˆ', '๐Ÿ”‰', '๐Ÿ”Š', '๐Ÿ“ข', '๐Ÿ“ฃ', '๐Ÿ“ฏ', '๐Ÿ””', '๐Ÿ”•',
892
+ '๐ŸŽผ', '๐ŸŽต', '๐ŸŽถ', '๐ŸŽ™๏ธ', '๐ŸŽš๏ธ', '๐ŸŽ›๏ธ', '๐ŸŽค', '๐ŸŽง', '๐Ÿ“ป', '๐ŸŽท',
893
+ '๐ŸŽธ', '๐ŸŽน', '๐ŸŽบ', '๐ŸŽป', '๐Ÿช•', '๐Ÿฅ', '๐Ÿ“ฑ', '๐Ÿ“ฒ', 'โ˜Ž๏ธ', '๐Ÿ“ž',
894
+ '๐Ÿ“Ÿ', '๐Ÿ“ ', '๐Ÿ”‹', '๐Ÿ”Œ', '๐Ÿ’ป', '๐Ÿ–ฅ๏ธ', '๐Ÿ–จ๏ธ', 'โŒจ๏ธ', '๐Ÿ–ฑ๏ธ', '๐Ÿ–ฒ๏ธ',
895
+ '๐Ÿ’ฝ', '๐Ÿ’พ', '๐Ÿ’ฟ', '๐Ÿ“€', '๐Ÿงฎ', '๐ŸŽฅ', '๐ŸŽž๏ธ', '๐Ÿ“ฝ๏ธ', '๐ŸŽฌ', '๐Ÿ“บ',
896
+ '๐Ÿ“ท', '๐Ÿ“ธ', '๐Ÿ“น', '๐Ÿ“ผ', '๐Ÿ”', '๐Ÿ”Ž', '๐Ÿ•ฏ๏ธ', '๐Ÿ’ก', '๐Ÿ”ฆ', '๐Ÿฎ',
897
+ '๐Ÿช”', '๐Ÿ“”', '๐Ÿ“•', '๐Ÿ“–', '๐Ÿ“—', '๐Ÿ“˜', '๐Ÿ“™', '๐Ÿ“š', '๐Ÿ““', '๐Ÿ“’',
898
+ '๐Ÿ“ƒ', '๐Ÿ“œ', '๐Ÿ“„', '๐Ÿ“ฐ', '๐Ÿ—ž๏ธ', '๐Ÿ“‘', '๐Ÿ”–', '๐Ÿท๏ธ', '๐Ÿ’ฐ', '๐Ÿ’ด',
899
+ '๐Ÿ’ต', '๐Ÿ’ถ', '๐Ÿ’ท', '๐Ÿ’ธ', '๐Ÿ’ณ', '๐Ÿงพ', '๐Ÿ’น', 'โœ‰๏ธ', '๐Ÿ“ง', '๐Ÿ“จ',
900
+ '๐Ÿ“ฉ', '๐Ÿ“ค', '๐Ÿ“ฅ', '๐Ÿ“ฆ', '๐Ÿ“ซ', '๐Ÿ“ช', '๐Ÿ“ฌ', '๐Ÿ“ญ', '๐Ÿ“ฎ', '๐Ÿ—ณ๏ธ',
901
+ 'โœ๏ธ', 'โœ’๏ธ', '๐Ÿ–‹๏ธ', '๐Ÿ–Š๏ธ', '๐Ÿ–Œ๏ธ', '๐Ÿ–๏ธ', '๐Ÿ“', '๐Ÿ’ผ', '๐Ÿ“', '๐Ÿ“‚',
902
+ '๐Ÿ—‚๏ธ', '๐Ÿ“…', '๐Ÿ“†', '๐Ÿ—’๏ธ', '๐Ÿ—“๏ธ', '๐Ÿ“‡', '๐Ÿ“ˆ', '๐Ÿ“‰', '๐Ÿ“Š', '๐Ÿ“‹',
903
+ '๐Ÿ“Œ', '๐Ÿ“', '๐Ÿ“Ž', '๐Ÿ–‡๏ธ', '๐Ÿ“', '๐Ÿ“', 'โœ‚๏ธ', '๐Ÿ—ƒ๏ธ', '๐Ÿ—„๏ธ', '๐Ÿ—‘๏ธ',
904
+ '๐Ÿ”’', '๐Ÿ”“', '๐Ÿ”', '๐Ÿ”', '๐Ÿ”‘', '๐Ÿ—๏ธ', '๐Ÿ”จ', '๐Ÿช“', 'โ›๏ธ', 'โš’๏ธ',
905
+ '๐Ÿ› ๏ธ', '๐Ÿ—ก๏ธ', 'โš”๏ธ', '๐Ÿ”ซ', '๐Ÿน', '๐Ÿ›ก๏ธ', '๐Ÿ”ง', '๐Ÿ”ฉ', 'โš™๏ธ', '๐Ÿ—œ๏ธ',
906
+ 'โš–๏ธ', '๐Ÿฆฏ', '๐Ÿ”—', 'โ›“๏ธ', '๐Ÿงฐ', '๐Ÿงฒ', 'โš—๏ธ', '๐Ÿงช', '๐Ÿงซ', '๐Ÿงฌ',
907
+ '๐Ÿ”ฌ', '๐Ÿ”ญ', '๐Ÿ“ก', '๐Ÿ’‰', '๐Ÿฉธ', '๐Ÿ’Š', '๐Ÿฉน', '๐Ÿฉบ', '๐Ÿšช', '๐Ÿ›๏ธ',
908
+ '๐Ÿ›‹๏ธ', '๐Ÿช‘', '๐Ÿšฝ', '๐Ÿšฟ', '๐Ÿ›', '๐Ÿช’', '๐Ÿงด', '๐Ÿงท', '๐Ÿงน', '๐Ÿงบ',
909
+ '๐Ÿงป', '๐Ÿงผ', '๐Ÿงฝ', '๐Ÿงฏ', '๐Ÿ›’', '๐Ÿšฌ', 'โšฐ๏ธ', 'โšฑ๏ธ', '๐Ÿ—ฟ', '๐Ÿง',
910
+ '๐Ÿšฎ', '๐Ÿšฐ', 'โ™ฟ', '๐Ÿšน', '๐Ÿšบ', '๐Ÿšป', '๐Ÿšผ', '๐Ÿšพ', '๐Ÿ›‚', '๐Ÿ›ƒ',
911
+ '๐Ÿ›„', '๐Ÿ›…', 'โš ๏ธ', '๐Ÿšธ', 'โ›”', '๐Ÿšซ', '๐Ÿšณ', '๐Ÿšญ', '๐Ÿšฏ', '๐Ÿšฑ',
912
+ '๐Ÿšท', '๐Ÿ“ต', '๐Ÿ”ž', 'โ˜ข๏ธ', 'โ˜ฃ๏ธ', 'โฌ†๏ธ', 'โ†—๏ธ', 'โžก๏ธ', 'โ†˜๏ธ', 'โฌ‡๏ธ',
913
+ 'โ†™๏ธ', 'โฌ…๏ธ', 'โ†–๏ธ', 'โ†•๏ธ', 'โ†”๏ธ', 'โ†ฉ๏ธ', 'โ†ช๏ธ', 'โคด๏ธ', 'โคต๏ธ', '๐Ÿ”ƒ',
914
+ '๐Ÿ”„', '๐Ÿ”™', '๐Ÿ”š', '๐Ÿ”›', '๐Ÿ”œ', '๐Ÿ”', '๐Ÿ›', 'โš›๏ธ', '๐Ÿ•‰๏ธ', 'โœก๏ธ',
915
+ 'โ˜ธ๏ธ', 'โ˜ฏ๏ธ', 'โœ๏ธ', 'โ˜ฆ๏ธ', 'โ˜ช๏ธ', 'โ˜ฎ๏ธ', '๐Ÿ•Ž', '๐Ÿ”ฏ', 'โ™ˆ', 'โ™‰',
916
+ 'โ™Š', 'โ™‹', 'โ™Œ', 'โ™', 'โ™Ž', 'โ™', 'โ™', 'โ™‘', 'โ™’', 'โ™“', 'โ›Ž',
917
+ '๐Ÿ”€', '๐Ÿ”', '๐Ÿ”‚', 'โ–ถ๏ธ', 'โฉ', 'โญ๏ธ', 'โฏ๏ธ', 'โ—€๏ธ', 'โช', 'โฎ๏ธ',
918
+ '๐Ÿ”ผ', 'โซ', '๐Ÿ”ฝ', 'โฌ', 'โธ๏ธ', 'โน๏ธ', 'โบ๏ธ', 'โ๏ธ', '๐ŸŽฆ', '๐Ÿ”…',
919
+ '๐Ÿ”†', '๐Ÿ“ถ', '๐Ÿ“ณ', '๐Ÿ“ด', 'โ™€๏ธ', 'โ™‚๏ธ', 'โšง๏ธ', 'โœ–๏ธ', 'โž•', 'โž–',
920
+ 'โž—', 'โ™พ๏ธ', 'โ€ผ๏ธ', 'โ‰๏ธ', 'โ“', 'โ”', 'โ•', 'โ—', 'ใ€ฐ๏ธ', '๐Ÿ’ฑ',
921
+ '๐Ÿ’ฒ', 'โš•๏ธ', 'โ™ป๏ธ', 'โšœ๏ธ', '๐Ÿ”ฑ', '๐Ÿ“›', '๐Ÿ”ฐ', 'โญ•', 'โœ…', 'โ˜‘๏ธ',
922
+ 'โœ”๏ธ', 'โŒ', 'โŽ', 'โžฐ', 'โžฟ', 'ใ€ฝ๏ธ', 'โœณ๏ธ', 'โœด๏ธ', 'โ‡๏ธ', 'ยฉ๏ธ',
923
+ 'ยฎ๏ธ', 'โ„ข๏ธ', '#๏ธโƒฃ', '*๏ธโƒฃ', '0๏ธโƒฃ', '1๏ธโƒฃ', '2๏ธโƒฃ', '3๏ธโƒฃ', '4๏ธโƒฃ', '5๏ธโƒฃ',
924
+ '6๏ธโƒฃ', '7๏ธโƒฃ', '8๏ธโƒฃ', '9๏ธโƒฃ', '๐Ÿ”Ÿ', '๐Ÿ” ', '๐Ÿ”ก', '๐Ÿ”ข', '๐Ÿ”ฃ', '๐Ÿ”ค',
925
+ '๐Ÿ…ฐ๏ธ', '๐Ÿ†Ž', '๐Ÿ…ฑ๏ธ', '๐Ÿ†‘', '๐Ÿ†’', '๐Ÿ†“', 'โ„น๏ธ', '๐Ÿ†”', 'โ“‚๏ธ', '๐Ÿ†•',
926
+ '๐Ÿ†–', '๐Ÿ…พ๏ธ', '๐Ÿ†—', '๐Ÿ…ฟ๏ธ', '๐Ÿ†˜', '๐Ÿ†™', '๐Ÿ†š', '๐Ÿˆ', '๐Ÿˆ‚๏ธ', '๐Ÿˆท๏ธ',
927
+ '๐Ÿˆถ', '๐Ÿˆฏ', '๐Ÿ‰', '๐Ÿˆน', '๐Ÿˆš', '๐Ÿˆฒ', '๐Ÿ‰‘', '๐Ÿˆธ', '๐Ÿˆด', '๐Ÿˆณ',
928
+ 'ใŠ—๏ธ', 'ใŠ™๏ธ', '๐Ÿˆบ', '๐Ÿˆต', '๐Ÿ”ด', '๐ŸŸ ', '๐ŸŸก', '๐ŸŸข', '๐Ÿ”ต', '๐ŸŸฃ',
929
+ '๐ŸŸค', 'โšซ', 'โšช', '๐ŸŸฅ', '๐ŸŸง', '๐ŸŸจ', '๐ŸŸฉ', '๐ŸŸฆ', '๐ŸŸช', '๐ŸŸซ',
930
+ 'โฌ›', 'โฌœ', 'โ—ผ๏ธ', 'โ—ป๏ธ', 'โ—พ', 'โ—ฝ', 'โ–ช๏ธ', 'โ–ซ๏ธ', '๐Ÿ”ถ', '๐Ÿ”ท',
931
+ '๐Ÿ”ธ', '๐Ÿ”น', '๐Ÿ”บ', '๐Ÿ”ป', '๐Ÿ’ ', '๐Ÿ”˜', '๐Ÿ”ณ', '๐Ÿ”ฒ', '๐Ÿ', '๐Ÿšฉ',
932
+ '๐ŸŽŒ', '๐Ÿด', '๐Ÿณ๏ธ', '๐Ÿณ๏ธโ€๐ŸŒˆ', '๐Ÿดโ€โ˜ ๏ธ', '๐Ÿ‡ฆ๐Ÿ‡จ', '๐Ÿ‡ฆ๐Ÿ‡ฉ', '๐Ÿ‡ฆ๐Ÿ‡ช', '๐Ÿ‡ฆ๐Ÿ‡ซ', '๐Ÿ‡ฆ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ',
933
+ '๐Ÿ‡ฆ๐Ÿ‡ฎ', '๐Ÿ‡ฆ๐Ÿ‡ฑ', '๐Ÿ‡ฆ๐Ÿ‡ฒ', '๐Ÿ‡ฆ๐Ÿ‡ด', '๐Ÿ‡ฆ๐Ÿ‡ถ', '๐Ÿ‡ฆ๐Ÿ‡ท', '๐Ÿ‡ฆ๐Ÿ‡ธ', '๐Ÿ‡ฆ๐Ÿ‡น', '๐Ÿ‡ฆ๐Ÿ‡บ', '๐Ÿ‡ฆ๐Ÿ‡ผ',
934
+ '๐Ÿ‡ฆ๐Ÿ‡ฝ', '๐Ÿ‡ฆ๐Ÿ‡ฟ', '๐Ÿ‡ง๐Ÿ‡ฆ', '๐Ÿ‡ง๐Ÿ‡ง', '๐Ÿ‡ง๐Ÿ‡ฉ', '๐Ÿ‡ง๐Ÿ‡ช', '๐Ÿ‡ง๐Ÿ‡ซ', '๐Ÿ‡ง๐Ÿ‡ฌ', '๐Ÿ‡ง๐Ÿ‡ญ', '๐Ÿ‡ง๐Ÿ‡ฎ',
935
+ '๐Ÿ‡ง๐Ÿ‡ฏ', '๐Ÿ‡ง๐Ÿ‡ฑ', '๐Ÿ‡ง๐Ÿ‡ฒ', '๐Ÿ‡ง๐Ÿ‡ณ', '๐Ÿ‡ง๐Ÿ‡ด', '๐Ÿ‡ง๐Ÿ‡ถ', '๐Ÿ‡ง๐Ÿ‡ท', '๐Ÿ‡ง๐Ÿ‡ธ', '๐Ÿ‡ง๐Ÿ‡น', '๐Ÿ‡ง๐Ÿ‡ป',
936
+ '๐Ÿ‡ง๐Ÿ‡ผ', '๐Ÿ‡ง๐Ÿ‡พ', '๐Ÿ‡ง๐Ÿ‡ฟ', '๐Ÿ‡จ๐Ÿ‡ฆ', '๐Ÿ‡จ๐Ÿ‡จ', '๐Ÿ‡จ๐Ÿ‡ฉ', '๐Ÿ‡จ๐Ÿ‡ซ', '๐Ÿ‡จ๐Ÿ‡ฌ', '๐Ÿ‡จ๐Ÿ‡ญ', '๐Ÿ‡จ๐Ÿ‡ฎ',
937
+ '๐Ÿ‡จ๐Ÿ‡ฐ', '๐Ÿ‡จ๐Ÿ‡ฑ', '๐Ÿ‡จ๐Ÿ‡ฒ', '๐Ÿ‡จ๐Ÿ‡ณ', '๐Ÿ‡จ๐Ÿ‡ด', '๐Ÿ‡จ๐Ÿ‡ต', '๐Ÿ‡จ๐Ÿ‡ท', '๐Ÿ‡จ๐Ÿ‡บ', '๐Ÿ‡จ๐Ÿ‡ป', '๐Ÿ‡จ๐Ÿ‡ผ',
938
+ '๐Ÿ‡จ๐Ÿ‡ฝ', '๐Ÿ‡จ๐Ÿ‡พ', '๐Ÿ‡จ๐Ÿ‡ฟ'
939
+ ];
940
+
941
  // Display loading indicator
942
  function setLoading(isLoading) {
943
  state.isLoading = isLoading;
 
984
  }
985
  }
986
 
987
+ // Get random emoji for 404 errors
988
+ function getRandomEmoji() {
989
+ return randomEmojis[Math.floor(Math.random() * randomEmojis.length)];
990
+ }
991
+
992
+ // Toggle stats display
993
+ function toggleStats() {
994
+ state.statsVisible = !state.statsVisible;
995
+ elements.statsContent.classList.toggle('open', state.statsVisible);
996
+ elements.statsToggle.textContent = state.statsVisible ? 'Hide Stats' : 'Show Stats';
997
+
998
+ if (state.statsVisible && state.topOwners.length > 0) {
999
+ renderCreatorStats();
1000
+ }
1001
+ }
1002
+
1003
+ // Render creator stats chart
1004
+ function renderCreatorStats() {
1005
+ if (state.chartInstance) {
1006
+ state.chartInstance.destroy();
1007
+ }
1008
+
1009
+ const ctx = elements.creatorStatsChart.getContext('2d');
1010
+
1011
+ // Prepare data
1012
+ const labels = state.topOwners.map(item => item[0]);
1013
+ const data = state.topOwners.map(item => item[1]);
1014
+
1015
+ // Generate colors for bars
1016
+ const colors = [];
1017
+ for (let i = 0; i < labels.length; i++) {
1018
+ const hue = (i * 360 / labels.length) % 360;
1019
+ colors.push(`hsla(${hue}, 70%, 80%, 0.7)`);
1020
+ }
1021
+
1022
+ // Create chart
1023
+ state.chartInstance = new Chart(ctx, {
1024
+ type: 'bar',
1025
+ data: {
1026
+ labels: labels,
1027
+ datasets: [{
1028
+ label: 'Number of Spaces',
1029
+ data: data,
1030
+ backgroundColor: colors,
1031
+ borderColor: colors.map(color => color.replace('0.7', '1')),
1032
+ borderWidth: 1
1033
+ }]
1034
+ },
1035
+ options: {
1036
+ indexAxis: 'y',
1037
+ responsive: true,
1038
+ maintainAspectRatio: false,
1039
+ plugins: {
1040
+ legend: {
1041
+ display: false
1042
+ },
1043
+ tooltip: {
1044
+ callbacks: {
1045
+ title: function(tooltipItems) {
1046
+ return tooltipItems[0].label;
1047
+ },
1048
+ label: function(context) {
1049
+ return `Spaces: ${context.raw}`;
1050
+ }
1051
+ }
1052
+ }
1053
+ },
1054
+ scales: {
1055
+ x: {
1056
+ beginAtZero: true,
1057
+ title: {
1058
+ display: true,
1059
+ text: 'Number of Spaces'
1060
+ }
1061
+ },
1062
+ y: {
1063
+ title: {
1064
+ display: true,
1065
+ text: 'Creator ID'
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+ });
1071
+ }
1072
+
1073
  // Load spaces with timeout
1074
  async function loadSpaces(page = 0) {
1075
  setLoading(true);
 
1093
  state.spaces = data.spaces;
1094
  state.totalItems = data.total;
1095
  state.currentPage = page;
1096
+ state.topOwners = data.top_owners || [];
1097
 
1098
  renderGrid(data.spaces);
1099
  renderPagination();
1100
+
1101
+ // If stats are visible, update chart
1102
+ if (state.statsVisible && state.topOwners.length > 0) {
1103
+ renderCreatorStats();
1104
+ }
1105
  } catch (error) {
1106
  console.error('Error loading spaces:', error);
1107
 
 
1180
  elements.pagination.appendChild(nextButton);
1181
  }
1182
 
1183
+ // Handle iframe error and provide static site fallback with random emoji
1184
  function handleIframeError(iframe, owner, name, title) {
1185
  const container = iframe.parentNode;
1186
 
 
1188
  const errorPlaceholder = document.createElement('div');
1189
  errorPlaceholder.className = 'error-placeholder';
1190
 
1191
+ // Random emoji instead of error icon
1192
+ const errorEmoji = document.createElement('div');
1193
+ errorEmoji.className = 'error-emoji';
1194
+ errorEmoji.textContent = getRandomEmoji();
1195
+ errorPlaceholder.appendChild(errorEmoji);
1196
 
1197
  // Error message
1198
  const errorMessage = document.createElement('p');
 
1334
  state.staticModeAttempted[spaceKey] = true;
1335
  iframe.src = directUrl + '/index.html';
1336
  } else {
1337
+ // If static mode also failed, show error with random emoji
1338
  handleIframeError(iframe, owner, name, title);
1339
  }
1340
  };
 
1349
  iframeDoc.body.textContent.includes('404') ||
1350
  iframeDoc.body.textContent.includes('not found');
1351
 
1352
+ if (isErrorPage) {
1353
+ // For 404 errors, show random emoji
1354
+ if (!state.staticModeAttempted[spaceKey]) {
1355
+ // Try static mode first
1356
+ state.staticModeAttempted[spaceKey] = true;
1357
+ iframe.src = directUrl + '/index.html';
1358
+ } else {
1359
+ // If static mode already attempted and still failing, show random emoji
1360
+ handleIframeError(iframe, owner, name, title);
1361
+ }
1362
  }
1363
  } catch (e) {
1364
  // Cross-origin errors are expected, this generally means the iframe loaded
 
1421
  // Refresh button event listener
1422
  elements.refreshButton.addEventListener('click', () => loadSpaces(0));
1423
 
1424
+ // Stats toggle button event listener
1425
+ elements.statsToggle.addEventListener('click', toggleStats);
1426
+
1427
  // Mac buttons functionality (just for show)
1428
  document.querySelectorAll('.mac-button').forEach(button => {
1429
  button.addEventListener('click', function(e) {