| 捐赠用户 | 162 |捐赠金额 | 163 |捐赠时间 | 164 |续命时长 | 165 |备注 | 166 |
|---|---|---|---|---|
| 我 | 171 |¥365.00 | 172 |2025-02-15 09:11:14 | 173 |+1年 | 174 |自掏腰包 | 175 |
| 爱发电用户_f4749 | 179 |¥5.00 | 180 |2025-02-20 20:43:52 | 181 |+1个月 | 182 |- | 183 |
| 南风 | 186 |¥80.00 | 187 |2025-02-24 22:29:30 | 188 |+1年 | 189 |80够了应该( | 190 |
| 爱发电用户_902b4 | 193 |¥5.00 | 194 |2025-03-07 21:00:44 | 195 |+1个月 | 196 |Thank you for your work. | 197 |
| 南风 | 200 |¥10.00 | 201 |2025-04-06 22:07:04 | 202 |+2个月 | 203 |屁股肉站点一定要维护下去ᕦ(ò_óˇ)ᕤ | 204 |
| 南风 | 207 |¥10.00 | 208 |2025-06-01 19:02:18 | 209 |+2个月 | 210 |211 | |
| 爱发电用户_9fwm | 214 |¥10.00 | 215 |2025-06-11 05:02:38 | 216 |+2个月 | 217 |218 | |
| 爱发电用户_f4749 | 221 |¥5.00 | 222 |2025-07-05 09:30:56 | 223 |+1个月 | 224 |225 | |
| 爱发电用户_f4749 | 228 |¥10.00 | 229 |2025-07-08 17:44:38 | 230 |+2个月 | 231 |给你推荐一个网盘:https://pan.miaostars.com/quota 站点一定要维持下去啊! | 232 |
| 南风 | 235 |¥10.00 | 236 |2025-08-12 19:26::5 | 237 |+2个月 | 238 |239 | |
\d+)\))?(?:\s+(?P\d{4}/\d{2}/\d{2}))?\s*$')
30 | RE_NESTED_IMAGE_LINK = re.compile(r'\[!\[([^\]]*)\]\(([^)]+)\)\]\(([^)]+)\)') # groups: alt, img_src, url
31 | RE_MARKDOWN_LINK = re.compile(r'!\[([^\]]*)\]\(([^)]+)\)|\[([^\]]+)\]\(([^)]+)\)')
32 | RE_HTML_COMMENT = re.compile(r'', re.S)
33 | # remove entire ... sections (ignore their content)
34 | RE_DETAILS = re.compile(r'.*?', re.S | re.I)
35 | # HTML anchor with nested image:
36 | RE_HTML_A_IMG = re.compile(r']+href=[\"\\\'](?P[^\"\'\)]+)[\"\'\)][^>]*>\s*
]+src=[\"\'](?P[^\"\']+)[\"\'][^>]*>\s* ', re.I)
37 | # Generic HTML anchor
38 | RE_HTML_A = re.compile(r']+href=[\"\\\'](?P[^\"\'\)]+)[\"\'\)][^>]*>(?P.*?) ', re.I | re.S)
39 | # Generic img tag
40 | RE_IMG_TAG = re.compile(r'
]+src=[\"\\\'](?P[^\"\']+)[\"\'][^>]*>', re.I)
41 |
42 | SHIELDS_DOMAINS = ("img.shields.io", "shields.io")
43 |
44 |
45 | def remove_html_comments(text: str) -> str:
46 | """Remove HTML comments () from text."""
47 | return RE_HTML_COMMENT.sub('', text)
48 |
49 |
50 | def normalize_channel_name(text: str) -> str:
51 | key = re.sub(r'[^0-9a-zA-Z]+', '_', text.strip()).strip('_').lower()
52 | return key or 'unknown'
53 |
54 |
55 | def guess_mirror_key_from_url(url: str) -> str:
56 | """Return a short key for a mirror based on URL or hostname."""
57 | try:
58 | p = urlparse(url)
59 | host = p.netloc.lower()
60 | # common shortcuts
61 | if 'github.com' in host:
62 | return 'github'
63 | if '123pan' in host or '123' in host:
64 | return '123'
65 | if 'huang1111' in host:
66 | return 'huang1111'
67 | if 'cloud.139' in host or 'caiyun' in host:
68 | return 'caiyun'
69 | # yun.139.com and related 139 domains should map to caiyun
70 | if '139.com' in host or 'yun.139' in host:
71 | return 'caiyun'
72 | if '1drv' in host or 'onedrive' in host:
73 | return 'onedrive'
74 | # else take the first part of hostname
75 | return host.split('.')[0]
76 | except Exception:
77 | return 'link'
78 |
79 |
80 | def is_shield_url(url: str) -> bool:
81 | try:
82 | p = urlparse(url)
83 | return any(domain in p.netloc for domain in SHIELDS_DOMAINS)
84 | except Exception:
85 | return False
86 |
87 |
88 | from html.parser import HTMLParser
89 |
90 |
91 | class AnchorHTMLParser(HTMLParser):
92 | def __init__(self):
93 | super().__init__()
94 | # stack to handle nested tags and collect inner text per anchor
95 | self._anchor_stack: list[dict] = []
96 | self.links: list[tuple[str, str]] = []
97 |
98 | def handle_starttag(self, tag, attrs):
99 | tag = tag.lower()
100 | if tag == 'a':
101 | href = None
102 | title = None
103 | for k, v in attrs:
104 | if k.lower() == 'href':
105 | href = v
106 | if k.lower() == 'title':
107 | title = v
108 | # push a new anchor context
109 | self._anchor_stack.append({'href': href or '', 'title': title or '', 'text': ''})
110 | elif tag == 'img':
111 | # record image alt text to current anchor context if any
112 | alt = ''
113 | for k, v in attrs:
114 | if k.lower() == 'alt':
115 | alt = v or ''
116 | if self._anchor_stack and alt:
117 | # append alt text to anchor's text so it can be used as label
118 | self._anchor_stack[-1]['text'] += (' ' + alt)
119 |
120 | def handle_data(self, data):
121 | if self._anchor_stack:
122 | self._anchor_stack[-1]['text'] += data
123 |
124 | def handle_endtag(self, tag):
125 | if tag.lower() == 'a' and self._anchor_stack:
126 | ctx = self._anchor_stack.pop()
127 | text = (ctx.get('text') or '').strip()
128 | title = (ctx.get('title') or '').strip()
129 | href = (ctx.get('href') or '').strip()
130 | label = text or title or ''
131 | if href:
132 | self.links.append((label, href))
133 |
134 |
135 | def render_markdown_to_html(md_text):
136 | """Render Markdown to HTML. Prefer `markdown` package if available; otherwise use a minimal fallback that converts markdown links/images to anchors."""
137 | try:
138 | from markdown import markdown
139 | return markdown(md_text)
140 | except Exception:
141 | text = md_text
142 | # images -> anchors
143 | text = re.sub(r'!\[([^\]]*)\]\(([^)]+)\)', r'\1', text)
144 | # links -> anchors
145 | text = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1', text)
146 | return text
147 |
148 |
149 | def extract_links_from_html(html):
150 | parser = AnchorHTMLParser()
151 | parser.feed(html)
152 | return parser.links
153 |
154 |
155 | class VersionBlock:
156 | def __init__(self, heading: str):
157 | self.heading = heading
158 | self.version = ''
159 | self.code = None
160 | self.date = None
161 | self.lines: list[str] = []
162 | self.download_links: list[tuple[str, str]] = [] # (label, url)
163 |
164 | def parse_heading(self):
165 | m = RE_HEADING.match(self.heading)
166 | if not m:
167 | return False
168 | self.version = m.group('version')
169 | if not self.version.startswith('v'):
170 | self.version = 'v' + self.version
171 | code = m.group('code')
172 | self.code = int(code) if code else None
173 | self.date = m.group('date')
174 | return True
175 |
176 | def add_line(self, line: str):
177 | self.lines.append(line.rstrip())
178 |
179 | def extract_links_from_lines(self):
180 | for ln in self.lines:
181 | # A) handle HTML anchors that wrap an
(e.g.,
)
182 | for m in RE_HTML_A_IMG.finditer(ln):
183 | href = (m.group('href') or '').strip()
184 | src = (m.group('src') or '').strip()
185 | if not href:
186 | continue
187 | parsed = urlparse(href)
188 | if parsed.scheme not in ('http','https'):
189 | continue
190 | if is_shield_url(href):
191 | continue
192 | # try to get alt text from the img tag; if absent, use filename from src
193 | alt_m = re.search(r'alt=[\"\']([^\"\']+)[\"\']', m.group(0) or '', re.I)
194 | if alt_m:
195 | label = alt_m.group(1).strip()
196 | else:
197 | label = src.split('/')[-1]
198 | # if the image filename or src contains play/商店, prefer that as a hint
199 | if re.search(r'play|商店', (label + ' ' + src).lower()):
200 | # make label explicitly indicate play so downstream heuristics pick it up
201 | label = (label + ' Play').strip()
202 | self.download_links.append((label, href))
203 |
204 | # B) handle generic HTML anchors text
205 | for m in RE_HTML_A.finditer(ln):
206 | href = (m.group('href') or '').strip()
207 | if not href:
208 | continue
209 | parsed = urlparse(href)
210 | if parsed.scheme not in ('http','https'):
211 | continue
212 | if is_shield_url(href):
213 | continue
214 | # extract visible text if any (strip inner tags)
215 | inner = re.sub(r'<[^>]+>', '', m.group('text') or '').strip()
216 | self.download_links.append((inner, href))
217 |
218 | # C) handle nested image-inside-link markdown: [](url)
219 | for m in RE_NESTED_IMAGE_LINK.finditer(ln):
220 | alt = (m.group(1) or '').strip()
221 | img_src = (m.group(2) or '').strip()
222 | url = (m.group(3) or '').strip()
223 | # skip non-http links (anchors, local files)
224 | parsed = urlparse(url)
225 | if parsed.scheme not in ('http','https'):
226 | continue
227 | if is_shield_url(url):
228 | continue
229 | # prefer visible alt text; if alt doesn't indicate Play but image filename does, add Play hint
230 | label = alt or img_src.split('/')[-1]
231 | if re.search(r'play|商店', (label + ' ' + img_src).lower()):
232 | label = (label + ' Play').strip()
233 | self.download_links.append((label, url.strip()))
234 |
235 | # D) handle normal markdown links and images
236 | for match in RE_MARKDOWN_LINK.finditer(ln):
237 | # match groups for image: group1(text) group2(url)
238 | # match groups for link: group3(text) group4(url)
239 | if match.group(2):
240 | # image syntax:  -> ignore images (unless outer link captured above)
241 | url = match.group(2).strip()
242 | if is_shield_url(url):
243 | continue
244 | continue
245 | else:
246 | label = match.group(3) or ''
247 | url = (match.group(4) or '').strip()
248 | # skip anchors, fragments, local references and non-http(s) urls
249 | if not url:
250 | continue
251 | parsed = urlparse(url)
252 | if parsed.scheme not in ('http', 'https'):
253 | # ignore internal anchors and local links like ./VersionList_2.x or #top
254 | continue
255 | if is_shield_url(url):
256 | # links directly to shields (rare) - skip
257 | continue
258 | # record as potential download link
259 | self.download_links.append((label.strip(), url.strip()))
260 |
261 | # Also render the entire markdown block to HTML and parse any anchors there.
262 | try:
263 | md_block = '\n'.join(self.lines)
264 | html = render_markdown_to_html(md_block)
265 | for title, href in extract_links_from_html(html):
266 | if not href:
267 | continue
268 | parsed = urlparse(href)
269 | if parsed.scheme not in ('http', 'https'):
270 | continue
271 | if is_shield_url(href):
272 | continue
273 | # avoid duplicates
274 | if any(href == ex[1] for ex in self.download_links):
275 | continue
276 | self.download_links.append(((title or '').strip(), href.strip()))
277 | except Exception:
278 | # be conservative: if HTML rendering fails, continue with previously found links
279 | pass
280 |
281 | def build_changelog(self) -> list[str]:
282 | changelog: list[str] = []
283 | for ln in self.lines:
284 | s = ln.strip()
285 | if not s:
286 | continue
287 | # skip lines that are only badge/link markup (after removing links)
288 | s_no_links = RE_MARKDOWN_LINK.sub('', s)
289 | s_no_links = re.sub(r'<[^>]+>', '', s_no_links).strip()
290 | if s_no_links == '':
291 | continue
292 | # remove markdown links entirely from changelog text
293 | s = RE_MARKDOWN_LINK.sub('', s)
294 | # remove leftover bracket(parenthesis) fragments like '](http://...)' or broken link parts
295 | s = re.sub(r'\]\s*\([^\)]*\)', '', s)
296 | s = re.sub(r'!\[[^\]]*\]', '', s)
297 | # remove inline references to shield SVG tokens or badge filenames (e.g., '下载-brightgreen.svg?logo=...')
298 | s = re.sub(r'\S*svg\?logo=\S*', '', s)
299 | # strip remaining HTML tags (e.g.,
) and weird whitespace
300 | s = re.sub(r'<[^>]+>', '', s).strip()
301 | # remove leading bullets
302 | s = re.sub(r'^\s*[-*•]\s*', '', s)
303 | # final small cleanup: collapse multiple spaces
304 | s = re.sub(r'\s{2,}', ' ', s).strip()
305 | # skip lines that are only punctuation or separators (e.g., ")", "--", ")-->" etc.)
306 | if not s or re.match(r'^[\W_]+$', s):
307 | continue
308 | # skip lines that are just separators like '--' or '|'
309 | if re.match(r'^[\-|]{2,}$', s):
310 | continue
311 | changelog.append(s)
312 | return changelog
313 |
314 |
315 | def parse_markdown_versions(md_text: str, debug: bool = False) -> dict:
316 | """Parse markdown and return JSON-friendly dict eg {'version': 'v1', 'details': [...]}"""
317 | text = remove_html_comments(md_text)
318 | # remove blocks entirely (user requested: ignore their content)
319 | text = RE_DETAILS.sub('', text)
320 | lines = text.splitlines()
321 |
322 | blocks: list[VersionBlock] = []
323 | current: VersionBlock | None = None
324 |
325 | for ln in lines:
326 | # heading
327 | if RE_HEADING.match(ln):
328 | current = VersionBlock(ln)
329 | current.parse_heading()
330 | blocks.append(current)
331 | continue
332 | if current is None:
333 | continue
334 | current.add_line(ln)
335 |
336 | # process each block
337 | details = []
338 | for b in blocks:
339 | b.extract_links_from_lines()
340 | changelog = b.build_changelog()
341 | # Normalize to two channels only: 'taptap' and 'playstore'.
342 | # Heuristics:
343 | # - if label mentions 'play' -> playstore
344 | # - if any link in this version is an .obb file, then .obb/.apk links are grouped into playstore
345 | # - otherwise default to taptap
346 | downloads = {'taptap': {}, 'playstore': {}}
347 |
348 | # build temporary link objects with metadata
349 | link_objs = []
350 | md_block = '\n'.join(b.lines)
351 |
352 | def url_has_play_context(url: str) -> bool:
353 | """Return True if nearby markdown context suggests this link is a Play-store build."""
354 | try:
355 | lower_block = md_block.lower()
356 | idx = lower_block.find(url.lower())
357 | if idx >= 0:
358 | start = max(0, idx - 80)
359 | end = min(len(lower_block), idx + len(url) + 80)
360 | ctx = lower_block[start:end]
361 | else:
362 | ctx = lower_block
363 | return bool(re.search(r'play|play[_\s]?商店|play\s*版本|商店|google', ctx, re.I))
364 | except Exception:
365 | return False
366 |
367 | for label, url in b.download_links:
368 | lbl = (label or '').strip()
369 | u = (url or '').strip()
370 | if not u:
371 | continue
372 | parsed = urlparse(u)
373 | scheme = parsed.scheme.lower() if parsed.scheme else ''
374 | if scheme not in ('http', 'https'):
375 | continue
376 | is_apk = u.lower().endswith('.apk') or ('.apk' in parsed.path.lower())
377 | is_obb = '.obb' in u.lower() or 'obb' in lbl.lower()
378 | lower_lbl = lbl.lower()
379 | lower_u = u.lower()
380 | # label or URL hints that this is a Play Store build
381 | is_play_label = ('play' in lower_lbl) or ('play' in lower_u) or ('google' in lower_u) or ('play_商店' in lower_lbl)
382 | # also inspect nearby markdown context (fix cases where badge text says TapTap but context indicates Play)
383 | if url_has_play_context(u):
384 | is_play_label = True
385 |
386 | # derive mirror key: prefer URL-derived keys for known hosts (e.g., caiyun, huang1111)
387 | derived = guess_mirror_key_from_url(u)
388 | if derived and derived not in ('www', 'link'):
389 | mirror_key = derived
390 | else:
391 | m = re.search(r'([0-9A-Za-z_\-]{2,})', lbl)
392 | if m:
393 | mirror_key = re.sub(r'[^0-9a-zA-Z]+', '', m.group(1)).lower()
394 | else:
395 | mirror_key = derived or 'link'
396 |
397 | link_objs.append({'label': lbl, 'url': u, 'mirror': mirror_key, 'is_apk': is_apk, 'is_obb': is_obb, 'is_play_label': is_play_label, 'by_host': None})
398 |
399 | # determine if there are obb files in this version
400 | version_has_obb = any(x['is_obb'] for x in link_objs)
401 |
402 | for link in link_objs:
403 | # decide channel
404 | if link['is_play_label']:
405 | channel = 'playstore'
406 | elif version_has_obb and (link['is_apk'] or link['is_obb']):
407 | # apk + obb rule: these files belong to playstore
408 | channel = 'playstore'
409 | else:
410 | # default to taptap
411 | channel = 'taptap'
412 |
413 | downloads[channel].setdefault(link['mirror'], link['url'])
414 |
415 | # remove empty channels
416 | downloads = {k: v for k, v in downloads.items() if v}
417 |
418 | detail = {
419 | 'versionName': b.version,
420 | }
421 | if b.code is not None:
422 | detail['versionCode'] = b.code
423 | if b.date:
424 | detail['releaseDate'] = b.date
425 | if changelog:
426 | detail['changelog'] = changelog
427 | if downloads:
428 | detail['downloads'] = downloads
429 | details.append(detail)
430 |
431 | if debug:
432 | print(f"Parsed {b.version}: code={b.code} date={b.date} changelog_len={len(changelog)} downloads={sum(len(v) for v in downloads.values())}")
433 |
434 | return {'version': 'v1', 'details': details}
435 |
436 |
437 | def main():
438 | p = argparse.ArgumentParser()
439 | p.add_argument('--input', '-i', required=True, help='Input markdown file')
440 | p.add_argument('--output', '-o', required=True, help='Output JSON file')
441 | p.add_argument('--debug', '-d', action='store_true')
442 | args = p.parse_args()
443 |
444 | with open(args.input, 'r', encoding='utf-8') as f:
445 | md = f.read()
446 |
447 | data = parse_markdown_versions(md, debug=args.debug)
448 |
449 | with open(args.output, 'w', encoding='utf-8') as f:
450 | json.dump(data, f, ensure_ascii=False, indent=2)
451 |
452 | if args.debug:
453 | print(json.dumps(data, ensure_ascii=False, indent=2))
454 |
455 |
456 | if __name__ == '__main__':
457 | main()
458 |
--------------------------------------------------------------------------------
/assets/js/main.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', () => {
2 | const startScreen = document.getElementById('start-screen');
3 | const mainScreen = document.getElementById('main-screen');
4 | const versionSelector = document.querySelector('.version-selector');
5 | const versionList = document.getElementById('version-list');
6 | const currentVersionDisplay = document.getElementById('current-version-display');
7 | const backBtn = document.getElementById('back-btn');
8 | const infoBtn = document.getElementById('info-btn');
9 | const rksDisplay = document.querySelector('.rks');
10 | const searchInput = document.getElementById('version-search');
11 |
12 | // Announcement elements
13 | const notificationContainer = document.getElementById('notification-container');
14 | const announcementModal = document.getElementById('announcement-modal');
15 | const modalIcon = document.getElementById('modal-icon');
16 | const modalTitle = document.getElementById('modal-title');
17 | const modalBody = document.getElementById('modal-body');
18 | const closeAnnouncementBtn = document.getElementById('close-announcement');
19 |
20 | let currentDetails = []; // Store current major version's details for filtering
21 |
22 | // Randomize RKS
23 | const randomRKS = (Math.random() * (16.99 - 12.00) + 12.00).toFixed(2);
24 | rksDisplay.textContent = `RKS ${randomRKS}`;
25 |
26 | // Initial fetch for announcements
27 | fetchAnnouncements();
28 |
29 | // Panel elements
30 | const contentContainer = document.querySelector('.content-container');
31 | const detailPanel = document.getElementById('version-detail-panel');
32 | const detailTitle = document.getElementById('detail-title');
33 | const detailMeta = document.getElementById('detail-meta');
34 | const detailChangelog = document.getElementById('detail-changelog');
35 | const downloadGrid = document.getElementById('download-list');
36 | const downloadMessage = document.getElementById('download-message');
37 | const closeDetailMobile = document.getElementById('close-detail-mobile');
38 | const closeDetailDesktop = document.getElementById('close-detail-desktop');
39 |
40 | // Sound effects (optional, placeholders)
41 | const playTapSound = () => {
42 | // console.log('Tap sound');
43 | };
44 |
45 | // Start Screen Interaction
46 | startScreen.addEventListener('click', () => {
47 | playTapSound();
48 | startScreen.classList.remove('active');
49 | mainScreen.classList.add('active');
50 |
51 | // Load data when entering main screen
52 | loadMajorVersions();
53 | });
54 |
55 | // Back Button
56 | backBtn.addEventListener('click', () => {
57 | playTapSound();
58 | // If panel is open, close it first
59 | if (contentContainer.classList.contains('panel-open')) {
60 | closePanel();
61 | } else {
62 | mainScreen.classList.remove('active');
63 | startScreen.classList.add('active');
64 | }
65 | });
66 |
67 | // Mobile Close Button
68 | if (closeDetailMobile) {
69 | closeDetailMobile.addEventListener('click', () => {
70 | closePanel();
71 | });
72 | }
73 |
74 | // Desktop Close Button
75 | if (closeDetailDesktop) {
76 | closeDetailDesktop.addEventListener('click', () => {
77 | closePanel();
78 | });
79 | }
80 |
81 | // Close panel when clicking on the shadow mask
82 | contentContainer.addEventListener('click', (e) => {
83 | if (e.target === contentContainer && contentContainer.classList.contains('panel-open')) {
84 | closePanel();
85 | }
86 | });
87 |
88 | // Info Button
89 | if (infoBtn) {
90 | infoBtn.addEventListener('click', () => {
91 | playTapSound();
92 | showAbout();
93 | });
94 | }
95 |
96 | // Search Logic
97 | if (searchInput) {
98 | searchInput.addEventListener('input', (e) => {
99 | const query = e.target.value.toLowerCase().trim();
100 | if (!query) {
101 | renderVersionList(currentDetails);
102 | return;
103 | }
104 |
105 | const filtered = currentDetails.filter(ver => {
106 | const nameMatch = ver.versionName.toLowerCase().includes(query);
107 | const codeMatch = ver.versionCode.toString().toLowerCase().includes(query);
108 | const changelogMatch = ver.changelog && ver.changelog.some(line => line.toLowerCase().includes(query));
109 | return nameMatch || codeMatch || changelogMatch;
110 | });
111 |
112 | renderVersionList(filtered, true); // Pass true to skip animation delay for search results
113 | });
114 | }
115 |
116 | function showAbout() {
117 | closePanel();
118 | currentVersionDisplay.textContent = '关于项目';
119 |
120 | versionList.innerHTML = `
121 |
122 | 关于 Phigros History
123 |
124 | 《Phigros》是由 Pigeon Games(鸽游)开发的节奏类游戏。Pigeon Games 是由初创通过 bilibili 视频网站发起的、由众多节奏类游戏爱好者组成的完全用爱发电的项目组。我们希望 Phigros 新颖的游戏模式和精心制作的插画与关卡可以让你感受到节奏类游戏的魅力。
125 | 本项目致力于收集并整理 Phigros 的历史版本及更新日志,方便玩家回顾与下载。
126 | 特别感谢 Cao Huy Hoang 提供的部分历史版本数据,没有他的帮助,该项目不可能发展得如此庞大。
127 | 此项目和 Pigeon Games(鸽游)、Apple Inc等公司及其关联方无任何关系,所有内容均来自公开渠道,仅供学习和研究使用。如有侵权,请联系我们删除相关内容。
128 |
129 | 支持我们
130 |
131 | 这是一个完全由爱好者维护的非营利项目。由于所有的上游存储开始收取更加高昂的费用,项目存储的费用和花销超出了我们所能承受的范围。如果您想支持我们继续提供免费的服务,请考虑捐赠。感谢您的支持!
132 |
133 |
134 | 支持我们
135 |
136 |
137 | 关于此页面
138 |
139 | 此页面是最近开发的实验性浏览器,用于浏览 Phigros 的历史版本。尚未广泛测试,可能存在一些问题。
140 | 您也可以访问我们的旧版网页,不过它将在未来被弃用。
141 | 如果您发现任何问题或有改进建议,请随时通过 GitHub 提交 issue 或 pull request。
142 |
143 |
144 |
145 | 访问 GitHub 仓库
146 |
147 |
148 |
149 | 报告问题
150 |
151 |
152 | 浙ICP备2025213066号-1
153 |
154 | `;
155 |
156 | // Unselect any active major version buttons
157 | document.querySelectorAll('.major-version-btn').forEach(b => b.classList.remove('active'));
158 | }
159 |
160 | function closePanel() {
161 | contentContainer.classList.remove('panel-open');
162 | // Remove active state from cards
163 | document.querySelectorAll('.version-card').forEach(c => c.classList.remove('active'));
164 | }
165 |
166 | async function loadMajorVersions() {
167 | try {
168 | const response = await fetch('api/v1/versions/index.json');
169 | if (!response.ok) throw new Error('无法加载版本索引');
170 |
171 | const data = await response.json();
172 | renderMajorVersions(data.versions);
173 |
174 | if (data.versions.length > 0) {
175 | const latest = data.versions[data.versions.length - 1];
176 | selectMajorVersion(latest);
177 | }
178 | } catch (error) {
179 | console.error('Error loading versions:', error);
180 | versionSelector.innerHTML = `
181 |
182 |
183 | 加载失败
184 |
185 | `;
186 | }
187 | }
188 |
189 | function renderMajorVersions(versions) {
190 | versionSelector.innerHTML = '';
191 | [...versions].reverse().forEach(v => {
192 | const btn = document.createElement('div');
193 | btn.className = 'major-version-btn';
194 | btn.innerHTML = `${v.version}`;
195 | btn.addEventListener('click', () => {
196 | playTapSound();
197 | selectMajorVersion(v);
198 | });
199 | v.element = btn;
200 | versionSelector.appendChild(btn);
201 | });
202 | }
203 |
204 | async function selectMajorVersion(versionObj) {
205 | closePanel(); // Close panel when switching major versions
206 |
207 | // Clear search when switching major versions
208 | if (searchInput) searchInput.value = '';
209 |
210 | document.querySelectorAll('.major-version-btn').forEach(b => b.classList.remove('active'));
211 | if (versionObj.element) versionObj.element.classList.add('active');
212 |
213 | currentVersionDisplay.textContent = versionObj.version;
214 | versionList.innerHTML = `
215 |
216 |
217 | 加载中...
218 |
219 | `;
220 |
221 | try {
222 | // Use the URL directly from the version object as requested
223 | // If it starts with /, we make it relative to ensure it works in subpaths
224 | let jsonPath = versionObj.url;
225 | if (jsonPath.startsWith('/')) {
226 | jsonPath = jsonPath.substring(1);
227 | }
228 |
229 | const response = await fetch(jsonPath);
230 | if (!response.ok) throw new Error(`无法加载 ${versionObj.version} 的详情`);
231 |
232 | const data = await response.json();
233 | currentDetails = data.details || [];
234 | renderVersionList(currentDetails);
235 | } catch (error) {
236 | console.error(error);
237 | versionList.innerHTML = `
238 |
239 |
240 | 加载版本详情失败
241 | ${error.message}
242 |
243 | `;
244 | }
245 | }
246 |
247 | function renderVersionList(details, isSearch = false) {
248 | versionList.innerHTML = '';
249 |
250 | if (!details || details.length === 0) {
251 | versionList.innerHTML = `
252 |
253 |
254 | 在此版本分支中未找到匹配项
255 |
256 | `;
257 | return;
258 | }
259 |
260 | const now = new Date();
261 | const thirtyDaysAgo = new Date();
262 | thirtyDaysAgo.setDate(now.getDate() - 30);
263 |
264 | details.forEach((ver, index) => {
265 | const card = document.createElement('div');
266 | card.className = 'version-card';
267 |
268 | // Check if downloads exist and are not empty
269 | const hasDownloads = ver.downloads && Object.keys(ver.downloads).length > 0;
270 |
271 | if (!hasDownloads) {
272 | card.classList.add('disabled');
273 | }
274 |
275 | // Check for icons
276 | let iconsHtml = '';
277 |
278 | // 1. Effective content tag (yellow)
279 | if (ver.tag && ver.tag.length > 0) {
280 | iconsHtml += ``;
281 | }
282 |
283 | // 2. Recent release (green) - within 30 days
284 | const releaseDate = new Date(ver.releaseDate);
285 | if (releaseDate >= thirtyDaysAgo) {
286 | iconsHtml += ``;
287 | }
288 |
289 | if (isSearch) {
290 | card.style.animation = 'none';
291 | card.style.opacity = '1';
292 | } else {
293 | card.style.animationDelay = `${index * 0.05}s`;
294 | }
295 |
296 | const changelogHtml = ver.changelog ? ver.changelog.join('
') : '暂无更新日志。';
297 |
298 | card.innerHTML = `
299 |
300 |
301 |
302 | ${ver.versionName}
303 | ${iconsHtml}
304 |
305 | ${ver.releaseDate}
306 |
307 | ${changelogHtml}
308 |
309 | `;
310 |
311 | card.addEventListener('click', () => {
312 | playTapSound();
313 | // Highlight active card
314 | document.querySelectorAll('.version-card').forEach(c => c.classList.remove('active'));
315 | card.classList.add('active');
316 |
317 | showVersionDetails(ver);
318 | });
319 |
320 | versionList.appendChild(card);
321 | });
322 | }
323 |
324 | function showVersionDetails(ver) {
325 | detailTitle.textContent = ver.versionName;
326 | detailMeta.textContent = `发布日期: ${ver.releaseDate} | 版本号: ${ver.versionCode}`;
327 |
328 | // April Fools Warning
329 | let aprilFoolsHtml = '';
330 | if (ver.tag && ver.tag.includes('april-fools')) {
331 | aprilFoolsHtml = 'new_releases此版本为特别的愚人节版本,包含仅在4月1日有效的内容!';
332 | }
333 |
334 | // Full Changelog
335 | if (ver.changelog && ver.changelog.length > 0) {
336 | detailChangelog.innerHTML = `${aprilFoolsHtml}${ver.changelog.map(line => `- ${line}
`).join('')}
`;
337 | } else {
338 | detailChangelog.innerHTML = `${aprilFoolsHtml}暂无更新日志。
`;
339 | }
340 |
341 | downloadGrid.innerHTML = '';
342 | downloadMessage.textContent = '';
343 |
344 | const hasDownloads = ver.downloads && Object.keys(ver.downloads).length > 0;
345 |
346 | if (hasDownloads) {
347 | const mirrorMap = {
348 | "123": "123网盘",
349 | "caiyun": "彩云网盘",
350 | "huang1111": "huang1111 网盘",
351 | "lanzou": "蓝奏云",
352 | "onedrive": "OneDrive",
353 | "google": "Google Drive",
354 | "mega": "MEGA"
355 | };
356 |
357 | // Iterate over sources (e.g., taptap, googleplay)
358 | for (const [source, mirrors] of Object.entries(ver.downloads)) {
359 | const groupDiv = document.createElement('div');
360 | groupDiv.className = 'download-group';
361 |
362 | const sourceTitle = document.createElement('h4');
363 | // Capitalize first letter
364 | sourceTitle.textContent = source.charAt(0).toUpperCase() + source.slice(1);
365 | groupDiv.appendChild(sourceTitle);
366 |
367 | const gridDiv = document.createElement('div');
368 | gridDiv.className = 'download-grid';
369 |
370 | // mirrors is an object like { "123": "url", "caiyun": "url" }
371 | if (typeof mirrors === 'object' && mirrors !== null) {
372 | for (const [mirrorKey, url] of Object.entries(mirrors)) {
373 | if (url) {
374 | const btn = document.createElement('a');
375 | btn.className = 'phigros-btn';
376 | btn.href = url;
377 | btn.target = '_blank';
378 |
379 | const mirrorName = mirrorMap[mirrorKey] || mirrorKey;
380 |
381 | btn.innerHTML = `
382 |
383 | ${mirrorName}
384 | `;
385 | gridDiv.appendChild(btn);
386 | }
387 | }
388 | } else if (typeof mirrors === 'string') {
389 | // Fallback if structure is flat
390 | const btn = document.createElement('a');
391 | btn.className = 'phigros-btn';
392 | btn.href = mirrors;
393 | btn.target = '_blank';
394 | btn.innerHTML = `
395 |
396 | 下载
397 | `;
398 | gridDiv.appendChild(btn);
399 | }
400 |
401 | if (gridDiv.children.length > 0) {
402 | groupDiv.appendChild(gridDiv);
403 | downloadGrid.appendChild(groupDiv);
404 | }
405 | }
406 | } else {
407 | downloadMessage.innerHTML = `
408 |
409 |
410 | 未找到该版本的应用包。如果您有可用的版本,请与我们分享。
411 |
412 | `;
413 | }
414 |
415 | // Open panel
416 | contentContainer.classList.add('panel-open');
417 | }
418 |
419 | async function fetchAnnouncements() {
420 | try {
421 | const response = await fetch('api/glados.json');
422 | if (!response.ok) return;
423 |
424 | const data = await response.json();
425 | if (data.announce && Array.isArray(data.announce)) {
426 | data.announce.forEach(ann => {
427 | const type = ann.type || 'info';
428 | const content = ann.content || ann.message; // Support both content and message keys
429 |
430 | if (type === 'error' || type === 'error_persist') {
431 | showAnnouncementModal(type, content);
432 | } else {
433 | showNotification(type, content);
434 | }
435 | });
436 | }
437 | } catch (error) {
438 | console.error('Failed to fetch announcements:', error);
439 | }
440 | }
441 |
442 | function showNotification(type, message) {
443 | const notification = document.createElement('div');
444 | notification.className = `notification ${type}`;
445 | notification.innerHTML = `
446 |
447 |
448 | ${message}
449 |
450 | `;
451 |
452 | notificationContainer.appendChild(notification);
453 |
454 | // Auto remove after 5 seconds
455 | setTimeout(() => {
456 | notification.classList.add('fade-out');
457 | setTimeout(() => notification.remove(), 500);
458 | }, 5000);
459 | }
460 |
461 | function showAnnouncementModal(type, message) {
462 | modalBody.textContent = message;
463 |
464 | if (type === 'error_persist') {
465 | modalIcon.textContent = 'report';
466 | modalIcon.style.color = '#ff0055';
467 | modalTitle.textContent = '严重错误';
468 | closeAnnouncementBtn.style.display = 'none';
469 | } else {
470 | modalIcon.textContent = 'error';
471 | modalIcon.style.color = '#ffcc00';
472 | modalTitle.textContent = '系统提示';
473 | closeAnnouncementBtn.style.display = 'flex';
474 | }
475 |
476 | announcementModal.classList.add('active');
477 | }
478 |
479 | if (closeAnnouncementBtn) {
480 | closeAnnouncementBtn.addEventListener('click', () => {
481 | announcementModal.classList.remove('active');
482 | });
483 | }
484 | });
485 |
--------------------------------------------------------------------------------
/api/v1/versions/1.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "v1",
3 | "details": [
4 | {
5 | "versionName": "v1.6.11",
6 | "versionCode": 50,
7 | "releaseDate": "2021/07/25",
8 | "changelog": [
9 | "1、一首全新单曲:",
10 | "energy trixxx"
11 | ],
12 | "downloads": {
13 | "taptap": {
14 | "huang1111": "https://pan.huang1111.cn/s/gg9wqUQ",
15 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.6.11/Phigros_V1.6.11_clone.apk"
16 | }
17 | }
18 | },
19 | {
20 | "versionName": "v1.6.10",
21 | "versionCode": 49,
22 | "releaseDate": "2021/07/08",
23 | "changelog": [
24 | "1、两首全新单曲:",
25 | "Apocalyptic",
26 | "游园地",
27 | "2、添加登陆入口。暂未开放注册功能,请以offline模式登陆。"
28 | ],
29 | "downloads": {
30 | "taptap": {
31 | "huang1111": "https://pan.huang1111.cn/s/1Q9Bxfv"
32 | }
33 | }
34 | },
35 | {
36 | "versionName": "v1.6.9",
37 | "versionCode": 48,
38 | "releaseDate": "2021/06/17",
39 | "changelog": [
40 | "1、两首全新单曲:",
41 | "Unorthodox Thoughts",
42 | "With You",
43 | "2、修复了一些设备的音频问题。因音频系统改动,更新后请重新调节音频延迟。"
44 | ],
45 | "downloads": {
46 | "taptap": {
47 | "huang1111": "https://pan.huang1111.cn/s/y5gAjt6"
48 | }
49 | }
50 | },
51 | {
52 | "versionName": "v1.6.8",
53 | "versionCode": 47,
54 | "releaseDate": "2021/05/27",
55 | "changelog": [
56 | "1、新增Muse Dash精选集,共3首歌曲:",
57 | "XING",
58 | "Final Step!",
59 | "Cthugha",
60 | "2、修复部分关卡等级显示错误的问题。"
61 | ],
62 | "downloads": {
63 | "taptap": {
64 | "huang1111": "https://pan.huang1111.cn/s/jRD6zty"
65 | }
66 | }
67 | },
68 | {
69 | "versionName": "v1.6.7.1",
70 | "versionCode": 46,
71 | "releaseDate": "2021/05/07",
72 | "changelog": [
73 | "1、修复了若干BUG",
74 | "2、优化触摸、判定系统。"
75 | ],
76 | "downloads": {
77 | "taptap": {
78 | "huang1111": "https://pan.huang1111.cn/s/zM6nNuM"
79 | }
80 | }
81 | },
82 | {
83 | "versionName": "v1.6.7",
84 | "versionCode": 45,
85 | "releaseDate": "2021/04/29",
86 | "changelog": [
87 | "1、新增WAVEAT精选集,包含4首歌曲:",
88 | "Initialize",
89 | "桜樹街道",
90 | "Get Ready!!",
91 | "volcanic",
92 | "2、一首全新单曲:",
93 | "Better Graphic Animation"
94 | ],
95 | "downloads": {
96 | "taptap": {
97 | "huang1111": "https://pan.huang1111.cn/s/2vQENhN"
98 | }
99 | }
100 | },
101 | {
102 | "versionName": "v1.6.6",
103 | "versionCode": 44,
104 | "releaseDate": "2021/04/14",
105 | "changelog": [
106 | "1、两首全新单曲:",
107 | "Eltaw",
108 | "大和撫子 -Wild Dances-",
109 | "2、修复了部分谱面中结尾Hold时间超过歌曲总时长的问题。",
110 | "3、修复了部分Legacy谱面信息显示错误的问题。"
111 | ],
112 | "downloads": {
113 | "taptap": {
114 | "huang1111": "https://pan.huang1111.cn/s/9QydETd"
115 | }
116 | }
117 | },
118 | {
119 | "versionName": "v1.6.5",
120 | "versionCode": 42,
121 | "releaseDate": "2021/03/29",
122 | "tag":["april-fools"],
123 | "changelog": [
124 | "1、一首全新单曲:",
125 | "Wavetapper",
126 | "2、修复了部分信息显示错误的问题。",
127 | "3、修复了Legacy谱面无法进入的问题。",
128 | "4、除此之外没有任何新内容,请放心游玩。"
129 | ],
130 | "downloads": {
131 | "taptap": {
132 | "huang1111": "https://pan.huang1111.cn/s/Qzk1mUm"
133 | },
134 | "playstore":{
135 | "huang1111": "https://pan.huang1111.cn/s/NkvkGT1"
136 | }
137 | }
138 | },
139 | {
140 | "versionName": "v1.6.4",
141 | "versionCode": 41,
142 | "releaseDate": "2021/03/03",
143 | "changelog": [
144 | "1、新增姜米條精选集,包含3首歌曲:",
145 | "Shadow",
146 | "Anomaly",
147 | "Destination",
148 | "2、修复了部分设备加载图片素材时画面可能会出现卡顿的问题。",
149 | "3、Lyrith -迷宮リリス- IN谱面轻微修改。",
150 | "4、Pixel Rebelz曲绘替换为第一版。",
151 | "5、改正modulus的IN谱面的难度数值。"
152 | ],
153 | "downloads": {
154 | "taptap": {
155 | "huang1111": "https://pan.huang1111.cn/s/k26aDcB"
156 | }
157 | }
158 | },
159 | {
160 | "versionName": "v1.6.3",
161 | "versionCode": 39,
162 | "releaseDate": "2021/01/13",
163 | "changelog": [
164 | "1、新增两首全新单曲:",
165 | "Cervelle Connexion",
166 | "modulus",
167 | "2、修复「Ποσειδών」无法正常游玩的问题。"
168 | ],
169 | "downloads": {
170 | "taptap": {
171 | "huang1111": "https://pan.huang1111.cn/s/MNenBhx"
172 | }
173 | }
174 | },
175 | {
176 | "versionName": "v1.6.2",
177 | "versionCode": 38,
178 | "releaseDate": "2020/12/20",
179 | "changelog": [
180 | "圣诞节快乐!",
181 | "1、三首全新单曲:",
182 | "Äventyr",
183 | "Träne",
184 | "雪降り、メリクリ",
185 | "2、全新的单曲选择页面。"
186 | ]
187 | },
188 | {
189 | "versionName": "v1.6.1",
190 | "versionCode": 37,
191 | "releaseDate": "2020/12/10",
192 | "changelog": [
193 | "1、更新一首单曲",
194 | "Hardcore Kwaya",
195 | "2、Sein [IN] 谱面微调"
196 | ]
197 | },
198 | {
199 | "versionName": "v1.6.0",
200 | "versionCode": 36,
201 | "releaseDate": "2020/11/26",
202 | "changelog": [
203 | "1、新增支线章节一,包含5首歌曲:",
204 | "Ποσειδών",
205 | "WATER",
206 | "Miracle Forest (VIP Mix)",
207 | "MOBILYS",
208 | "Lyrith -迷宮リリス-",
209 | "2、修复了画面正中央无法点击的问题。",
210 | "3、修复了[PRAW]、MARENOL的IN难度中部分Hold的判定问题。",
211 | "4、更换了商店页面歌曲。"
212 | ],
213 | "downloads": {
214 | "taptap": {
215 | "huang1111": "https://pan.huang1111.cn/s/qg2eku3"
216 | }
217 | }
218 | },
219 | {
220 | "versionName": "v1.5.7",
221 | "versionCode": 35,
222 | "releaseDate": "2020/11/12",
223 | "changelog": [
224 | "1、新增两首全新单曲:",
225 | "SIGMA",
226 | "Electron",
227 | "2、修复了多个BUG,优化了音频系统。请前往设置页面重新调整谱面延时。"
228 | ],
229 | "downloads": {
230 | "taptap": {
231 | "huang1111": "https://pan.huang1111.cn/s/O8XqGfL"
232 | }
233 | }
234 | },
235 | {
236 | "versionName": "v1.5.6",
237 | "versionCode": 34,
238 | "releaseDate": "2020/11/09",
239 | "changelog": [
240 | "一系列BUG修复。"
241 | ]
242 | },
243 | {
244 | "versionName": "v1.5.5",
245 | "versionCode": 32,
246 | "releaseDate": "2020/10/29",
247 | "changelog": [
248 | "一系列BUG修复。"
249 | ]
250 | },
251 | {
252 | "versionName": "v1.5.3",
253 | "versionCode": 31,
254 | "releaseDate": "2020/10/21",
255 | "changelog": [
256 | "1、新增两首全新单曲:",
257 | "Starduster",
258 | "Parallel Retrogression(Game Ver.)",
259 | "2、修改了音频系统",
260 | "由于音频系统的变动,大部分玩家需要重新调整谱面延时。请在【设置】-【谱面延时】中调整。"
261 | ],
262 | "downloads": {
263 | "taptap": {
264 | "huang1111": "https://pan.huang1111.cn/s/3eo8nHm"
265 | }
266 | }
267 | },
268 | {
269 | "versionName": "v1.5.3",
270 | "versionCode": 30,
271 | "releaseDate": "2020/10/14",
272 | "changelog": [
273 | "1、新增两首全新单曲:",
274 | "Starduster",
275 | "Parallel Retrogression(Game Ver.)",
276 | "2、修改了音频系统"
277 | ],
278 | "downloads": {
279 | "taptap": {
280 | "huang1111": "https://pan.huang1111.cn/s/eNvxmfg"
281 | }
282 | }
283 | },
284 | {
285 | "versionName": "v1.5.2",
286 | "versionCode": 29,
287 | "releaseDate": "2020/09/18",
288 | "changelog": [
289 | "1、新增两首全新单曲:",
290 | "Orthodox",
291 | "End Me",
292 | "2、修复了一个可能导致玩家无法进入单曲集的BUG。",
293 | "3、部分修改关卡添加Legacy谱面。"
294 | ]
295 | },
296 | {
297 | "versionName": "v1.5.1",
298 | "versionCode": 28,
299 | "releaseDate": "2020/09/08",
300 | "changelog": [
301 | "Bug修复。"
302 | ]
303 | },
304 | {
305 | "versionName": "v1.5.0",
306 | "versionCode": 27,
307 | "releaseDate": "2020/08/28",
308 | "changelog": [
309 | "1、更新主线章节第六章,包含5首曲目:",
310 | "Colorful Days♪",
311 | "micro.wav",
312 | "重生",
313 | "NO ONE YES MAN",
314 | "望影の方舟Six",
315 | "2、与《同步音律喵赛克》的联动!一首全新联动曲目:",
316 | "The Mountain Eater",
317 | "2、新增了第二章收集品,以及一些额外章节收集品。",
318 | "3、一大批较老的关卡进行了翻新,其中有一些谱面进行了完全的重置。",
319 | "4、游戏中的引导做了大量的优化,新手找不到路的时代终结了!"
320 | ],
321 | "downloads": {
322 | "taptap": {
323 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.5.0/Phigros_V1.5.0_clone.apk"
324 | }
325 | }
326 | },
327 | {
328 | "versionName": "v1.5.0",
329 | "versionCode": 26,
330 | "releaseDate": "2020/08/28",
331 | "changelog": [
332 | "1、更新主线章节第六章,包含5首曲目:",
333 | "Colorful Days♪",
334 | "micro.wav",
335 | "重生",
336 | "NO ONE YES MAN",
337 | "望影の方舟Six",
338 | "2、与《同步音律喵赛克》的联动!一首全新联动曲目:",
339 | "The Mountain Eater",
340 | "2、新增了第二章收集品,以及一些额外章节收集品。",
341 | "3、一大批较老的关卡进行了翻新,其中有一些谱面进行了完全的重置。",
342 | "4、游戏中的引导做了大量的优化,新手找不到路的时代终结了!"
343 | ]
344 | },
345 | {
346 | "versionName": "v1.4.7",
347 | "versionCode": 25,
348 | "releaseDate": "2020/07/15",
349 | "changelog": [
350 | "1、新增了 GOOD系列 精选集,包含五首全新曲目:",
351 | "GOODTEK",
352 | "GOODBOUNCE",
353 | "GOODWORLD",
354 | "GOODFORTUNE",
355 | "GOODRAGE",
356 | "2、优化游戏在某些设备上的流畅度。"
357 | ],
358 | "downloads": {
359 | "taptap": {
360 | "huang1111": "https://pan.huang1111.cn/s/5XgLPUl"
361 | }
362 | }
363 | },
364 | {
365 | "versionName": "v1.4.6",
366 | "versionCode": 24,
367 | "releaseDate": "2020/06/29",
368 | "changelog": [
369 | "1、主线章节五中新增一首全新歌曲:",
370 | "Leave All Behind",
371 | "在通关主线章节5后就会出现。。"
372 | ],
373 | "downloads": {
374 | "taptap": {
375 | "huang1111": "https://pan.huang1111.cn/s/4R2B3Cg",
376 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.4.6/phigros_V1.4.6_clone.apk"
377 | }
378 | }
379 | },
380 | {
381 | "versionName": "v1.4.5.1",
382 | "versionCode": 23,
383 | "releaseDate": "2020/06/22",
384 | "changelog": [
385 | "一系列BUG修复。"
386 | ]
387 | },
388 | {
389 | "versionName": "v1.4.5",
390 | "versionCode": 22,
391 | "releaseDate": "2020/06/15",
392 | "changelog": [
393 | "1、新增一首全新单曲:",
394 | "Palescreen",
395 | "2、一系列BUG修复。",
396 | "3、优化了软件存储空间占用(因此需要存储空间权限,请勿慌张)。"
397 | ]
398 | },
399 | {
400 | "versionName": "v1.4.4",
401 | "versionCode": 21,
402 | "releaseDate": "2020/05/29",
403 | "changelog": [
404 | "1、新增一首全新单曲:",
405 | "Get Back",
406 | "2、一系列BUG修复。"
407 | ],
408 | "downloads": {
409 | "taptap": {
410 | "huang1111": "https://pan.huang1111.cn/s/bywdaHY"
411 | }
412 | }
413 | },
414 | {
415 | "versionName": "v1.4.3",
416 | "versionCode": 20,
417 | "releaseDate": "2020/05/18",
418 | "changelog": [
419 | "1、为庆祝TapTap平台突破50w下载量,全部单曲半价优惠!",
420 | "2、新增两首全新单曲",
421 | "Speed Up!",
422 | "Magenta Potion",
423 | "3、全新的选曲页面UI,以及大量的视觉调整。",
424 | "4、现在新进入游戏的玩家可以在第一次通过教程关卡时获得32MB+基础奖励(最高32.125MB)的奖励(需分数超过880000)",
425 | "5、现在第一次查看新收集品时将获得512KB的奖励"
426 | ],
427 | "downloads": {
428 | "taptap": {
429 | "huang1111": "https://pan.huang1111.cn/s/A63dvhB"
430 | }
431 | }
432 | },
433 | {
434 | "versionName": "v1.4.3",
435 | "versionCode": 19,
436 | "releaseDate": "2020/05/18",
437 | "changelog": [
438 | "1、为庆祝TapTap平台突破50w下载量,全部单曲半价优惠!",
439 | "2、新增两首全新单曲",
440 | "Speed Up!",
441 | "Magenta Potion",
442 | "3、全新的选曲页面UI,以及大量的视觉调整。",
443 | "4、现在新进入游戏的玩家可以在第一次通过教程关卡时获得32MB+基础奖励(最高32.125MB)的奖励(需分数超过880000)",
444 | "5、现在第一次查看新收集品时将获得512KB的奖励"
445 | ]
446 | },
447 | {
448 | "versionName": "v1.4.3",
449 | "versionCode": 19,
450 | "releaseDate": "2020/05/18",
451 | "changelog": [
452 | "1、为庆祝TapTap平台突破50w下载量,全部单曲半价优惠!",
453 | "2、新增两首全新单曲",
454 | "Speed Up!",
455 | "Magenta Potion",
456 | "3、全新的选曲页面UI,以及大量的视觉调整。",
457 | "4、现在新进入游戏的玩家可以在第一次通过教程关卡时获得32MB+基础奖励(最高32.125MB)的奖励(需分数超过880000)",
458 | "5、现在第一次查看新收集品时将获得512KB的奖励"
459 | ]
460 | },
461 | {
462 | "versionName": "v1.4.2",
463 | "versionCode": 18,
464 | "releaseDate": "2020/04/24",
465 | "changelog": [
466 | "1、新增了 HyuN 精选集,包含三首全新曲目:",
467 | "Infinity Heaven",
468 | "Disorder",
469 | "CROSS†SOUL"
470 | ],
471 | "downloads": {
472 | "taptap": {
473 | "huang1111": "https://pan.huang1111.cn/s/mxqVBS1"
474 | }
475 | }
476 | },
477 | {
478 | "versionName": "v1.4.1",
479 | "versionCode": 17,
480 | "releaseDate": "2020/03/30",
481 | "tag": ["april-fools"],
482 | "changelog": [
483 | "1、新增一首全新单曲:",
484 | "Dead Soul",
485 | "2、除此之外没有任何新内容,请放心游玩"
486 | ],
487 | "downloads": {
488 | "taptap": {
489 | "huang1111": "https://pan.huang1111.cn/s/75ekDSg",
490 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.4.1/Phigros_V1.4.1_clone.apk"
491 |
492 | },
493 | "playstore": {
494 | "huang1111": "https://pan.huang1111.cn/s/LENXT6"
495 | }
496 | }
497 | },
498 | {
499 | "versionName": "v1.4.0",
500 | "versionCode": 16,
501 | "releaseDate": "2020/03/18",
502 | "changelog": [
503 | "1、新增了 Rising Sun Traxx 精选集,包含五首全新曲目:",
504 | "Another Me",
505 | "mechanted",
506 | "life flashes before weeb eyes",
507 | "Break Through The Barrier",
508 | "Chronostasis",
509 | "2、一些视觉上的调整。"
510 | ],
511 | "downloads": {
512 | "playstore": {
513 | "huang1111": "https://pan.huang1111.cn/s/5X6lAul"
514 | }
515 | }
516 | },
517 | {
518 | "versionName": "v1.3.2",
519 | "versionCode": 15,
520 | "releaseDate": "2020/03/04",
521 | "changelog": [
522 | "新增两首全新单曲",
523 | "Next Time",
524 | "Rubbish Sorting"
525 | ],
526 | "downloads": {
527 | "playstore": {
528 | "huang1111": "https://pan.huang1111.cn/s/4R67WSg"
529 | }
530 | }
531 | },
532 | {
533 | "versionName": "v1.3.1",
534 | "versionCode": 14,
535 | "releaseDate": "2020/02/29",
536 | "changelog": [
537 | "1、新增了Google Play排行榜。",
538 | "2、修复了章节选择界面可能无法正常显示的问题。",
539 | "3、修复了某个收集品无法被收集到的问题。",
540 | "4、一系列收集品文本修改。",
541 | "5、修复了第一次进入隐藏曲目时打击音无法正常播放的问题。",
542 | "6、Data mining调整。"
543 | ],
544 | "downloads": {
545 | "playstore": {
546 | "huang1111": "https://pan.huang1111.cn/s/A6zaRSB"
547 | }
548 | }
549 | },
550 | {
551 | "versionName": "v1.3.0",
552 | "versionCode": 12,
553 | "releaseDate": "2020/02/20",
554 | "changelog": [
555 | "1.3.0版本更新",
556 | "1、新增了主线第五章,包含五首曲目",
557 | "NYA!!! -by FLuoRiTe/姜米條",
558 | "JunXion Between Life And Death(VIP Mix) -by 1N6Fs",
559 | "cryout -by Ju_E",
560 | "Reimei -by 影虎。",
561 | "尊師 ~The Guru~ -by rider",
562 | "2、新增了收集品系统。",
563 | "3、在商店中新增了抽奖系统。",
564 | "4、设置中新增了音频问题修复页面,若你的设备存在音频问题,请前往设置中查看该页面。",
565 | "5、一些视觉上的调整"
566 | ],
567 | "downloads": {
568 | "taptap": {
569 | "huang1111": "https://pan.huang1111.cn/s/E7x6LHb",
570 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.3.0/phigros_v1.3.0_clone.apk"
571 |
572 | },
573 | "playstore": {
574 | "huang1111": "https://pan.huang1111.cn/s/mxwOvF1"
575 | }
576 | }
577 | },
578 | {
579 | "versionName": "[GHOST RELEASE]",
580 | "versionCode": 11,
581 | "releaseDate": "[GHOST RELEASE]",
582 | "changelog": [
583 | "(事实上,此版本不存在在版本历史中)"
584 | ],
585 | "downloads": {
586 | "playstore": {
587 | "huang1111": "https://pan.huang1111.cn/s/75RE6Hg"
588 | }
589 | }
590 | },
591 | {
592 | "versionName": "v1.2.5",
593 | "versionCode": 10,
594 | "releaseDate": "2020/01/22",
595 | "changelog": [
596 | "1、祝各位玩家春节快乐!",
597 | "2、新增全新单曲:",
598 | "Aleph-0",
599 | "3、一些视觉上的调整"
600 | ]
601 | },
602 | {
603 | "versionName": "v1.2.4",
604 | "versionCode": 9,
605 | "releaseDate": "2019/12/20",
606 | "changelog": [
607 | "圣诞快乐!新增了两首全新单曲:",
608 | "Snow Desert",
609 | "Burn"
610 | ],
611 | "downloads": {
612 | "taptap": {
613 | "huang1111": "https://pan.huang1111.cn/s/8Qyx8fQ"
614 | }
615 | }
616 | },
617 | {
618 | "versionName": "v1.2.4",
619 | "versionCode": 9,
620 | "releaseDate": "2019/12/20",
621 | "changelog": [
622 | "1、圣诞节快乐!新增了全新单曲:",
623 | "Snow Desert",
624 | "Burn"
625 | ],
626 | "downloads": {
627 | "taptap": {
628 | "huang1111": "https://pan.huang1111.cn/s/8Qyx8fQ"
629 | }
630 | }
631 | },
632 | {
633 | "versionName": "v1.2.3",
634 | "versionCode": 8,
635 | "releaseDate": "2019/12/04",
636 | "changelog": [
637 | "1、新增了一首全新单曲",
638 | "Khronostasis Katharsis",
639 | "2、新增了插画商店与头像商店。",
640 | "3、修复了一处翻译错误。",
641 | "4、一些视觉方面的微调。"
642 | ]
643 | },
644 | {
645 | "versionName": "v1.2.2",
646 | "versionCode": 7,
647 | "releaseDate": "2019/11/22",
648 | "changelog": [
649 | "1、新增了一首全新单曲",
650 | "Sparkle New Life",
651 | "2、修复了部分玩家服务费数额显示错误的Bug。",
652 | "3、为新增的商店优化了引导。",
653 | "4、全新的打击动效。"
654 | ]
655 | },
656 | {
657 | "versionName": "v1.2.1",
658 | "versionCode": 6,
659 | "releaseDate": "2019/11/22",
660 | "changelog": [
661 | "1、新增了一首全新单曲",
662 | "Sparkle New Life",
663 | "2、修复了部分玩家服务费数额显示错误的Bug。",
664 | "3、为新增的商店优化了引导。",
665 | "4、全新的打击动效。"
666 | ]
667 | },
668 | {
669 | "versionName": "v1.2.1",
670 | "versionCode": 6,
671 | "releaseDate": "2019/11/16",
672 | "changelog": [
673 | "1、更新了三首全新单曲:",
674 | "Find_Me",
675 | "Sein",
676 | "狂喜蘭舞",
677 | "2、新增了“单曲集”和“商店”。",
678 | "3、继续尝试尝试修复音频问题。如果您的设备依然存在音频问题,请在TapTap社区的“音频问题”专题帖下留言。"
679 | ]
680 | },
681 | {
682 | "versionName": "v1.2.0",
683 | "versionCode": 5,
684 | "releaseDate": "2019/10/23",
685 | "changelog": [
686 | "1.2.0更新",
687 | "1、更新了“闫东炜精选集2”,包含三首曲目:",
688 | "Alphasia",
689 | "華灯爱",
690 | "开心病",
691 | "2、原统计界面升级为“个人名片”。",
692 | "3、尝试修复了十余款机型的音频问题。如果您的设备依然存在音频问题,请在TapTap社区的“音频问题”专题帖下留言。"
693 | ],
694 | "downloads": {
695 | "taptap": {
696 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/download/v1.2.0/phigros_V1.2.0_clone.apk"
697 | }
698 | }
699 | },
700 | {
701 | "versionName": "v1.1.1",
702 | "versionCode": 4,
703 | "releaseDate": "2019/10/11",
704 | "changelog": [
705 | "1.1.1更新:",
706 | "大幅优化大部分设备音频延时,请更新后在设置中重新调整。若更新后存在音频问题,请在社区专题贴中留言"
707 | ],
708 | "downloads": {
709 | "taptap": {
710 | "huang1111": "https://pan.huang1111.cn/s/G8qdxiW"
711 | }
712 | }
713 | },
714 | {
715 | "versionName": "v1.1.0",
716 | "versionCode": 3,
717 | "releaseDate": "2019/09/26",
718 | "changelog": [
719 | "1、新增了主线章节四。",
720 | "五首全新曲目——",
721 | "由Monst Death创作的Sultan Rage",
722 | "由Antistar feat. Ctymax创作的Class Memories",
723 | "由Itsuki创作的-SURREALISM-",
724 | "由Megalo_PaleWhite创作的Bonus Time",
725 | "由Tanchiky创作的Energy Synergy Matrix",
726 | "2、增加了FC/AP指示器,您可以在设置中开启。",
727 | "3、修复了一系列的BUG。"
728 | ],
729 | "downloads": {
730 | "taptap": {
731 | "huang1111": "https://pan.huang1111.cn/s/LxXkzU6"
732 | }
733 | }
734 | },
735 | {
736 | "versionName": "v1.1.0",
737 | "versionCode": 3,
738 | "releaseDate": "2019/09/25",
739 | "changelog": [
740 | "1.1.0版本更新:",
741 | "1、新增了主线章节四。",
742 | "五首全新曲目——",
743 | "由Monst Death创作的Sultan Rage",
744 | "由Antistar feat. Ctymax创作的Class Memories",
745 | "由Itsuki创作的-SURREALISM-",
746 | "由Megalo_PaleWhite创作的Bonus Time",
747 | "由Tanchiky创作的Energy Synergy Matrix",
748 | "2、增加了FC/AP指示器,您可以在设置中开启。",
749 | "3、修复了一系列的BUG。"
750 | ],
751 | "downloads": {
752 | "taptap": {
753 | "huang1111": "https://pan.huang1111.cn/s/LxXkzU6"
754 | }
755 | }
756 | },
757 | {
758 | "versionName": "v1.0.1",
759 | "versionCode": 2,
760 | "releaseDate": "2019/09/09",
761 | "changelog": [
762 | "1.0.1版本更新:",
763 | "1、修复了iPhone 6、iPhone 6 Plus、iPhone 5 C、iPad mini2等设备的闪退问题。",
764 | "2、延时调整的范围从0ms~500ms变为-400ms ~ 600ms。",
765 | "3、一些视觉上的调整。"
766 | ],
767 | "downloads": {
768 | "taptap": {
769 | "huang1111": "https://pan.huang1111.cn/s/K9knZhY"
770 | }
771 | }
772 | },
773 | {
774 | "versionName": "v1.0.0",
775 | "versionCode": 1,
776 | "releaseDate": "2019/08/29",
777 | "changelog": [
778 | "一些软件优化"
779 | ],
780 | "downloads": {
781 | "taptap": {
782 | "repacked": "https://github.com/SteveZMTstudios/Phigros-history/releases/tag/v1.0.0"
783 | }
784 | }
785 | },
786 | {
787 | "versionName": "v1.0.0",
788 | "versionCode": 1,
789 | "releaseDate": "2019/08/28",
790 | "changelog": [
791 | "更改了游戏关卡画面的初始亮度",
792 | "提取码:`stevezmt`"
793 | ],
794 | "downloads": {
795 | "taptap": {
796 | "huang1111": "https://pan.huang1111.cn/s/oX72lC8"
797 | }
798 | }
799 | }
800 | ]
801 | }
--------------------------------------------------------------------------------