diff options
| author | Pinapelz <yukais@pinapelz.com> | 2026-03-22 23:34:41 -0700 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2026-03-22 23:34:41 -0700 |
| commit | 8f859e4786a02fea69ec086814d4f667f2f01d5d (patch) | |
| tree | dd9311fda0071b6523c3771fffe60b54a9a70d5c | |
| parent | 791bd4dae497d887b23f7962751dca4c933c4ed4 (diff) | |
| parent | 491ec0a18a58384b3d74990161d69051353701db (diff) | |
Merge branch 'main' into v3
| -rw-r--r-- | bemani/sdvx.py | 76 | ||||
| -rw-r--r-- | constants.py | 2 | ||||
| -rw-r--r-- | generate.py | 2 | ||||
| -rw-r--r-- | middleware/package.json | 2 | ||||
| -rw-r--r-- | middleware/pnpm-lock.yaml | 94 | ||||
| -rw-r--r-- | middleware/public/favicon.ico | bin | 25931 -> 15406 bytes | |||
| -rw-r--r-- | middleware/src/app/[gameName]/page.tsx | 28 | ||||
| -rw-r--r-- | news_feed.py | 4 | ||||
| -rw-r--r-- | site/src/pages/Homepage.tsx | 6 | ||||
| -rw-r--r-- | summarizer.py | 2 |
10 files changed, 158 insertions, 58 deletions
diff --git a/bemani/sdvx.py b/bemani/sdvx.py index 9d5a33b..5a7d25c 100644 --- a/bemani/sdvx.py +++ b/bemani/sdvx.py @@ -49,3 +49,79 @@ def parse_exceed_gear_news_site(html: str): }) return entries + +def parse_nabla_news_site(html: str): + base_url = "https://p.eagate.573.jp" + soup = BeautifulSoup(html, 'html.parser') + news_list = soup.select('#news-inner ul.news li') + + entries = [] + for li in news_list: + strong_tags = li.select('strong') + if not strong_tags: + continue + + date = strong_tags[0] + date_str = date.text.strip() + try: + dt = datetime.strptime(date_str, "%Y.%m.%d") + timestamp = int(dt.timestamp()) + except ValueError: + timestamp = None + + headline_text = None + if len(strong_tags) > 1: + headline_text = strong_tags[1].text.strip() + + for tag in li.select('font, b, u, span'): + tag.unwrap() + + content_parts = [] + for node in li.contents: + if hasattr(node, 'name'): + if node.name == 'strong': + continue + elif node.name == 'br': + content_parts.append('\n') + elif node.name == 'a' and 'link-text' in node.get('class', []): + content_parts.append(node.text.strip()) + elif node.name not in ['img']: # Skip image tags for content + content_parts.append(node.get_text(strip=True)) + else: + text = str(node).strip() + if text and text not in [date_str, headline_text]: + content_parts.append(text) + + content = '\n'.join(filter(None, content_parts)).strip() + + images = [] + for img in li.select('img'): + src = img.get('data-original') or img.get('src') + if not src or (isinstance(src, str) and src.startswith('data:')): + continue + if isinstance(src, str): + src = urljoin(base_url, src) + parent = img.find_parent('a') + href = None + if parent and hasattr(parent, 'get') and parent.get('href'): + href_val = parent.get('href') + if isinstance(href_val, str): + href = urljoin(base_url, href_val) + + image_entry = {'image': src, 'link': href} + if image_entry not in images: + images.append(image_entry) + + entries.append({ + 'date': date_str, + 'identifier': 'SOUND_VOLTEX', + 'type': None, + 'timestamp': timestamp, + 'headline': headline_text, + 'content': content, + "url": None, + 'images': images, + 'is_ai_summary': False + }) + + return entries diff --git a/constants.py b/constants.py index dc1c680..06b752c 100644 --- a/constants.py +++ b/constants.py @@ -2,7 +2,7 @@ from enum import Enum DAYS_LIMIT=14 -SOUND_VOLTEX_EXCEED_GEAR_NEWS_SITE ="https://p.eagate.573.jp/game/sdvx/vi/news/index.html" +SOUND_VOLTEX_NABLA_NEWS_SITE="https://p.eagate.573.jp/game/sdvx/vii/news/index.html" POLARIS_CHORD_NEWS_SITE="https://p.eagate.573.jp/game/polarischord/pc/news/news.html" POLARIS_CHORD_RECENT_NEWS_LIMIT=15 diff --git a/generate.py b/generate.py index 819ce08..79c8f11 100644 --- a/generate.py +++ b/generate.py @@ -162,7 +162,7 @@ def generate_iidx_news_file(): return news def generate_sdvx_news_file(): - news = generate_news_file("sdvx_news", constants.SOUND_VOLTEX_EXCEED_GEAR_NEWS_SITE) + news = generate_news_file("sdvx_news", constants.SOUND_VOLTEX_NABLA_NEWS_SITE) attempt_broadcast_notifications(news, "New Information for SOUND VOLTEX","sdvx") return news diff --git a/middleware/package.json b/middleware/package.json index b54eb7a..cf4f007 100644 --- a/middleware/package.json +++ b/middleware/package.json @@ -20,7 +20,7 @@ "eslint": "9.30.0", "eslint-config-next": "15.3.4", "global": "^4.4.0", - "next": "^15.3.4", + "next": "15.3.6", "postcss": "^8.5.6", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/middleware/pnpm-lock.yaml b/middleware/pnpm-lock.yaml index 2e2f6e2..f82b66e 100644 --- a/middleware/pnpm-lock.yaml +++ b/middleware/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 0.15.15 '@next/font': specifier: 14.2.15 - version: 14.2.15(next@15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 14.2.15(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@types/node': specifier: 24.0.8 version: 24.0.8 @@ -28,7 +28,7 @@ importers: version: 1.35.4 '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 1.5.0(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) '@vercel/og': specifier: ^0.6.8 version: 0.6.8 @@ -42,8 +42,8 @@ importers: specifier: ^4.4.0 version: 4.4.0 next: - specifier: ^15.3.4 - version: 15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: 15.3.6 + version: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) postcss: specifier: ^8.5.6 version: 8.5.6 @@ -340,8 +340,8 @@ packages: '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} - '@next/env@15.3.4': - resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==} + '@next/env@15.3.6': + resolution: {integrity: sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw==} '@next/eslint-plugin-next@15.3.4': resolution: {integrity: sha512-lBxYdj7TI8phbJcLSAqDt57nIcobEign5NYIKCiy0hXQhrUbTqLqOaSDi568U6vFg4hJfBdZYsG4iP/uKhCqgg==} @@ -351,50 +351,50 @@ packages: peerDependencies: next: '*' - '@next/swc-darwin-arm64@15.3.4': - resolution: {integrity: sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg==} + '@next/swc-darwin-arm64@15.3.5': + resolution: {integrity: sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.3.4': - resolution: {integrity: sha512-Z0FYJM8lritw5Wq+vpHYuCIzIlEMjewG2aRkc3Hi2rcbULknYL/xqfpBL23jQnCSrDUGAo/AEv0Z+s2bff9Zkw==} + '@next/swc-darwin-x64@15.3.5': + resolution: {integrity: sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.3.4': - resolution: {integrity: sha512-l8ZQOCCg7adwmsnFm8m5q9eIPAHdaB2F3cxhufYtVo84pymwKuWfpYTKcUiFcutJdp9xGHC+F1Uq3xnFU1B/7g==} + '@next/swc-linux-arm64-gnu@15.3.5': + resolution: {integrity: sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.3.4': - resolution: {integrity: sha512-wFyZ7X470YJQtpKot4xCY3gpdn8lE9nTlldG07/kJYexCUpX1piX+MBfZdvulo+t1yADFVEuzFfVHfklfEx8kw==} + '@next/swc-linux-arm64-musl@15.3.5': + resolution: {integrity: sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.3.4': - resolution: {integrity: sha512-gEbH9rv9o7I12qPyvZNVTyP/PWKqOp8clvnoYZQiX800KkqsaJZuOXkWgMa7ANCCh/oEN2ZQheh3yH8/kWPSEg==} + '@next/swc-linux-x64-gnu@15.3.5': + resolution: {integrity: sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.3.4': - resolution: {integrity: sha512-Cf8sr0ufuC/nu/yQ76AnarbSAXcwG/wj+1xFPNbyNo8ltA6kw5d5YqO8kQuwVIxk13SBdtgXrNyom3ZosHAy4A==} + '@next/swc-linux-x64-musl@15.3.5': + resolution: {integrity: sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.3.4': - resolution: {integrity: sha512-ay5+qADDN3rwRbRpEhTOreOn1OyJIXS60tg9WMYTWCy3fB6rGoyjLVxc4dR9PYjEdR2iDYsaF5h03NA+XuYPQQ==} + '@next/swc-win32-arm64-msvc@15.3.5': + resolution: {integrity: sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.3.4': - resolution: {integrity: sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg==} + '@next/swc-win32-x64-msvc@15.3.5': + resolution: {integrity: sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1595,8 +1595,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@15.3.4: - resolution: {integrity: sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA==} + next@15.3.6: + resolution: {integrity: sha512-oI6D1zbbsh6JzzZFDCSHnnx6Qpvd1fSkVJu/5d8uluqnxzuoqtodVZjYvNovooznUq8udSAiKp7MbwlfZ8Gm6w==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -2299,38 +2299,38 @@ snapshots: '@neon-rs/load@0.0.4': {} - '@next/env@15.3.4': {} + '@next/env@15.3.6': {} '@next/eslint-plugin-next@15.3.4': dependencies: fast-glob: 3.3.1 - '@next/font@14.2.15(next@15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@next/font@14.2.15(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: - next: 15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@next/swc-darwin-arm64@15.3.4': + '@next/swc-darwin-arm64@15.3.5': optional: true - '@next/swc-darwin-x64@15.3.4': + '@next/swc-darwin-x64@15.3.5': optional: true - '@next/swc-linux-arm64-gnu@15.3.4': + '@next/swc-linux-arm64-gnu@15.3.5': optional: true - '@next/swc-linux-arm64-musl@15.3.4': + '@next/swc-linux-arm64-musl@15.3.5': optional: true - '@next/swc-linux-x64-gnu@15.3.4': + '@next/swc-linux-x64-gnu@15.3.5': optional: true - '@next/swc-linux-x64-musl@15.3.4': + '@next/swc-linux-x64-musl@15.3.5': optional: true - '@next/swc-win32-arm64-msvc@15.3.4': + '@next/swc-win32-arm64-msvc@15.3.5': optional: true - '@next/swc-win32-x64-msvc@15.3.4': + '@next/swc-win32-x64-msvc@15.3.5': optional: true '@nodelib/fs.scandir@2.1.5': @@ -2615,9 +2615,9 @@ snapshots: dependencies: uncrypto: 0.1.3 - '@vercel/analytics@1.5.0(next@15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@vercel/analytics@1.5.0(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': optionalDependencies: - next: 15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 '@vercel/og@0.6.8': @@ -3646,9 +3646,9 @@ snapshots: natural-compare@1.4.0: {} - next@15.3.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 15.3.4 + '@next/env': 15.3.6 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 @@ -3658,14 +3658,14 @@ snapshots: react-dom: 19.1.0(react@19.1.0) styled-jsx: 5.1.6(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.3.4 - '@next/swc-darwin-x64': 15.3.4 - '@next/swc-linux-arm64-gnu': 15.3.4 - '@next/swc-linux-arm64-musl': 15.3.4 - '@next/swc-linux-x64-gnu': 15.3.4 - '@next/swc-linux-x64-musl': 15.3.4 - '@next/swc-win32-arm64-msvc': 15.3.4 - '@next/swc-win32-x64-msvc': 15.3.4 + '@next/swc-darwin-arm64': 15.3.5 + '@next/swc-darwin-x64': 15.3.5 + '@next/swc-linux-arm64-gnu': 15.3.5 + '@next/swc-linux-arm64-musl': 15.3.5 + '@next/swc-linux-x64-gnu': 15.3.5 + '@next/swc-linux-x64-musl': 15.3.5 + '@next/swc-win32-arm64-msvc': 15.3.5 + '@next/swc-win32-x64-msvc': 15.3.5 sharp: 0.34.2 transitivePeerDependencies: - '@babel/core' diff --git a/middleware/public/favicon.ico b/middleware/public/favicon.ico Binary files differindex 718d6fe..a6bb8d8 100644 --- a/middleware/public/favicon.ico +++ b/middleware/public/favicon.ico diff --git a/middleware/src/app/[gameName]/page.tsx b/middleware/src/app/[gameName]/page.tsx index ed5361a..a46b09d 100644 --- a/middleware/src/app/[gameName]/page.tsx +++ b/middleware/src/app/[gameName]/page.tsx @@ -355,6 +355,20 @@ function NewsPostPage({ : `${mainNewsUrl}/game/${gameName}#${postId}` : null; + const createToggleUrl = () => { + const params = new URLSearchParams(); + params.set('post', postId); + + if (lang === 'en') { + } else { + params.set('lang', 'en'); + } + + const baseUrl = gameName === "news" ? "/news" : `/game/${gameName}`; + const queryString = params.toString(); + return queryString ? `${baseUrl}?${queryString}` : baseUrl; + }; + return ( <main className="min-h-screen text-white font-sans bg-black"> <div className="w-full max-w-xl mx-auto px-3 sm:px-4 py-5 box-border"> @@ -477,11 +491,21 @@ function NewsPostPage({ {/* Navigation Buttons */} <div className="mt-3 flex flex-col items-center gap-2.5 text-center"> + {/* Language Toggle Button */} + {(newsPost.en_headline || newsPost.en_content) && ( + <Link + href={createToggleUrl()} + className="block w-full max-w-xs bg-linear-to-br from-purple-500 to-purple-700 text-white px-5 py-3.5 rounded-md text-sm font-semibold shadow-md shadow-purple-500/30 no-underline border-0 transition-all duration-200 text-center hover:brightness-110 active:translate-y-px" + > + {lang === "en" ? "日本語で読む" : "Read in English"} + </Link> + )} + <Link href="/" - className="block w-full max-w-xs bg-gradient-to-br from-blue-500 to-blue-700 text-white px-5 py-3.5 rounded-md text-sm font-semibold shadow-md shadow-blue-500/30 no-underline border-0 transition-all duration-200 text-center hover:brightness-110 active:translate-y-px" + className="block w-full max-w-xs bg-linear-to-br from-blue-500 to-blue-700 text-white px-5 py-3.5 rounded-md text-sm font-semibold shadow-md shadow-blue-500/30 no-underline border-0 transition-all duration-200 text-center hover:brightness-110 active:translate-y-px" > - Back to 573 UPDATES + {lang === "en" ? "Back to 573 UPDATES" : "573 UPDATESに戻る"} </Link> </div> </div> diff --git a/news_feed.py b/news_feed.py index cb76ba8..1bcb240 100644 --- a/news_feed.py +++ b/news_feed.py @@ -48,11 +48,11 @@ def _attach_llm_summaries(news_posts: list, game_name: str): # BEMANI (Specific feeds because these provide better information) # --------------------------------------------------------------------------- -@registry.register(constants.SOUND_VOLTEX_EXCEED_GEAR_NEWS_SITE) +@registry.register(constants.SOUND_VOLTEX_NABLA_NEWS_SITE) class SoundVoltexSource(NewsSource): def fetch(self, version=None) -> list[dict]: from bemani.sdvx import parse_exceed_gear_news_site - site_data = download_site_as_html(constants.SOUND_VOLTEX_EXCEED_GEAR_NEWS_SITE) + site_data = download_site_as_html(constants.SOUND_VOLTEX_NABLA_NEWS_SITE) news_posts = sorted(parse_exceed_gear_news_site(site_data), key=lambda x: x['timestamp'], reverse=True) return translate.add_translate_text_to_en(news_posts, overrides=[("ボルテ", "SDVX")]) diff --git a/site/src/pages/Homepage.tsx b/site/src/pages/Homepage.tsx index 13ec4fd..24052d7 100644 --- a/site/src/pages/Homepage.tsx +++ b/site/src/pages/Homepage.tsx @@ -196,11 +196,11 @@ export default function Home() { viewBox="-271 273 256 256" xmlSpace="preserve" > - <g id="SVGRepo_bgCarrier" stroke-width="0"></g> + <g id="SVGRepo_bgCarrier" strokeWidth="0"></g> <g id="SVGRepo_tracerCarrier" - stroke-linecap="round" - stroke-linejoin="round" + strokeLinejoin="round" + strokeLinecap="round" ></g> <g id="SVGRepo_iconCarrier"> {" "} diff --git a/summarizer.py b/summarizer.py index 0c30347..689626d 100644 --- a/summarizer.py +++ b/summarizer.py @@ -90,5 +90,5 @@ def generate_headline_and_content_from_images(img_urls: list[str], game: str, me except openai.OpenAIError as e: print(f"[ERROR] Function call to OpenAI for summarization failed ERROR -> {e} ") database.close() - return None, None + return f"NEW {game} INFORMATION / 新しい{game}情報", f"NEW {game} INFORMATION AVAILABLE / 新しい{game}情報が利用可能です" return headline, content |
