refactor(i18n): 将 useTranslation 重命名为 getTranslation 以更准确描述功能
fix(依赖): 添加缺失的依赖项到 useEffect 钩子中 style(env): 更新 .gitignore 和 .env 文件配置 docs(docker): 更新 docker-compose 和文档中的镜像命名
This commit is contained in:
@@ -8,27 +8,27 @@
|
||||
|
||||
## 方法一:分别构建和运行 Docker 镜像
|
||||
|
||||
在 `Simple-Podcast-Script` 项目的根目录下执行以下命令来构建 Docker 镜像。
|
||||
在项目的根目录下执行以下命令来构建 Docker 镜像。
|
||||
|
||||
### 构建 Docker 镜像
|
||||
|
||||
#### 构建 Web 应用镜像
|
||||
|
||||
```bash
|
||||
docker build -t simple-podcast-web -f Dockerfile-Web .
|
||||
docker build -t podcast-web -f Dockerfile-Web .
|
||||
```
|
||||
|
||||
* `-t simple-podcast-web`:为镜像指定一个名称和标签。
|
||||
* `-t podcast-web`:为镜像指定一个名称和标签。
|
||||
* `-f Dockerfile-Web`:指定 Web 应用的 Dockerfile 路径。
|
||||
* `.`:指定构建上下文的路径,这里是项目的根目录。
|
||||
|
||||
#### 构建 Server 应用镜像
|
||||
|
||||
```bash
|
||||
docker build -t simple-podcast-server -f Dockerfile-Server .
|
||||
docker build -t podcast-server -f Dockerfile-Server .
|
||||
```
|
||||
|
||||
* `-t simple-podcast-server`:为镜像指定一个名称和标签。
|
||||
* `-t podcast-server`:为镜像指定一个名称和标签。
|
||||
* `-f Dockerfile-Server`:指定 Server 应用的 Dockerfile 路径。
|
||||
* `.`:指定构建上下文的路径,这里是项目的根目录。
|
||||
|
||||
@@ -39,7 +39,7 @@ docker build -t simple-podcast-server -f Dockerfile-Server .
|
||||
#### 运行 Web 应用容器
|
||||
|
||||
```bash
|
||||
docker run -d -p 3200:3000 -v /opt/audio:/app/server/output --restart always --name podcast-web simple-podcast-web
|
||||
docker run -d -p 3200:3000 -v /opt/audio:/app/server/output --restart always --name podcast-web podcast-web
|
||||
```
|
||||
|
||||
#### 命令说明:
|
||||
@@ -50,18 +50,18 @@ docker run -d -p 3200:3000 -v /opt/audio:/app/server/output --restart always --n
|
||||
* `-v /opt/sqlite.db:/app/web/sqlite.db`:将宿主机的 `/opt/sqlite.db` 文件挂载到容器内的 `/app/web/sqlite.db` 文件,用于数据库的持久化存储。
|
||||
* `--restart always`:设置容器的重启策略,确保容器在意外停止或系统重启后能自动重启。
|
||||
* `--name podcast-web`:为运行中的容器指定一个名称,方便后续管理。
|
||||
* `simple-podcast-web`:指定要运行的 Docker 镜像名称。
|
||||
* `podcast-web`:指定要运行的 Docker 镜像名称。
|
||||
|
||||
#### 运行 Server 应用容器
|
||||
|
||||
```bash
|
||||
docker run -d -p 3100:8000 -v /opt/audio:/app/server/output -v /opt/sqlite.db:/app/web/sqlite.db --restart always --name podcast-server simple-podcast-server
|
||||
docker run -d -p 3100:8000 -v /opt/audio:/app/server/output -v /opt/sqlite.db:/app/web/sqlite.db --restart always --name podcast-server podcast-server
|
||||
```
|
||||
|
||||
或者,如果您的应用程序需要配置环境变量(例如 `PODCAST_API_SECRET_KEY`),您可以使用 `-e` 参数进行设置:
|
||||
|
||||
```bash
|
||||
docker run -d -p 3100:8000 -v /opt/audio:/app/server/output --restart always --name podcast-server -e PODCAST_API_SECRET_KEY="your-production-api-secret-key" simple-podcast-server
|
||||
docker run -d -p 3100:8000 -v /opt/audio:/app/server/output --restart always --name podcast-server -e PODCAST_API_SECRET_KEY="your-production-api-secret-key" podcast-server
|
||||
```
|
||||
|
||||
#### 命令说明:
|
||||
@@ -72,7 +72,7 @@ docker run -d -p 3100:8000 -v /opt/audio:/app/server/output --restart always --n
|
||||
* `--restart always`:设置容器的重启策略,确保容器在意外停止或系统重启后能自动重启。
|
||||
* `--name podcast-server`:为运行中的容器指定一个名称,方便后续管理。
|
||||
* `-e PODCAST_API_SECRET_KEY="your-production-api-secret-key"`:设置环境变量,将 `"your-production-api-secret-key"` 替换为您的实际密钥。
|
||||
* `simple-podcast-server`:指定要运行的 Docker 镜像名称。
|
||||
* `podcast-server`:指定要运行的 Docker 镜像名称。
|
||||
|
||||
## 方法二:使用 Docker Compose(推荐)
|
||||
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
version: '3.8'
|
||||
version: '1.0.0'
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-Web
|
||||
image: simple-podcast-web
|
||||
image: podcast-web
|
||||
ports:
|
||||
- "3200:3000"
|
||||
volumes:
|
||||
- audio-data/output:/app/server/output
|
||||
- audio-data/sqlite.db:/app/web/sqlite.db
|
||||
- /opt/audio/output:/app/server/output
|
||||
- /opt/audio/sqlite.db:/app/web/sqlite.db
|
||||
restart: always
|
||||
depends_on:
|
||||
- server
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://server:8000
|
||||
|
||||
server:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-Server
|
||||
image: simple-podcast-server
|
||||
image: podcast-server
|
||||
ports:
|
||||
- "3100:8000"
|
||||
volumes:
|
||||
- audio-data/output:/app/server/output
|
||||
- /opt/audio/output:/app/server/output
|
||||
restart: always
|
||||
environment:
|
||||
- PODCAST_API_SECRET_KEY=your-production-api-secret-key
|
||||
|
||||
volumes:
|
||||
audio-data:
|
||||
24
web/.env
Normal file
24
web/.env
Normal file
@@ -0,0 +1,24 @@
|
||||
# 应用配置
|
||||
NEXT_PUBLIC_APP_NAME=PodcastHub
|
||||
NEXT_PUBLIC_APP_VERSION=1.0.0
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
# 开发模式配置
|
||||
NODE_ENV=development
|
||||
|
||||
NEXT_PUBLIC_PODCAST_API_BASE_URL=http://localhost:8000
|
||||
NEXT_PUBLIC_PODCAST_CALLBACK_URL=http://localhost:3000/api/points
|
||||
NEXT_PUBLIC_ENABLE_TTS_CONFIG_PAGE=true
|
||||
POINTS_PER_PODCAST=20
|
||||
POINTS_PER_PODCAST_INIT=100
|
||||
POINTS_PER_SIGN_IN=40
|
||||
ALLOWED_USER_IDS="123456890"
|
||||
|
||||
TTS_PROVIDERS_NAME="tts_providers.json"
|
||||
DB_FILE_NAME="file:sqlite.db"
|
||||
BETTER_AUTH_SECRET=qwertyuiop
|
||||
BETTER_AUTH_URL=http://localhost:3000 #Base URL of your app.
|
||||
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
GITHUB_ID=
|
||||
GITHUB_SECRET=
|
||||
2
web/.gitignore
vendored
2
web/.gitignore
vendored
@@ -26,7 +26,7 @@ yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import React, { use } from 'react';
|
||||
import React from 'react'; // 不再需要 use
|
||||
import { Metadata } from 'next';
|
||||
import { AiOutlineTikTok, AiFillQqCircle, AiOutlineGithub, AiOutlineTwitter, AiFillMail } from 'react-icons/ai';
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../../lib/utils';
|
||||
import { getTranslation } from '../../../i18n';
|
||||
|
||||
export async function generateMetadata({ params }: { params: { lang: string } }): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const { t } = await (await import('../../../i18n')).useTranslation(lang, 'contact');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
export type paramsType = Promise<{ lang: string }>;
|
||||
|
||||
// 您的 generateMetadata 函数是完全正确的,无需改动
|
||||
export async function generateMetadata({ params }: { params: paramsType }): Promise<Metadata> {
|
||||
const { lang } = await params; // 直接解构
|
||||
const { t } = await getTranslation(lang, 'contact');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang); // 直接调用 headers()
|
||||
return {
|
||||
title: t('contact_us_title'),
|
||||
description: t('contact_us_description'),
|
||||
@@ -17,16 +21,16 @@ export async function generateMetadata({ params }: { params: { lang: string } })
|
||||
};
|
||||
}
|
||||
|
||||
import { useTranslation } from '../../../i18n'; // 导入服务端的 useTranslation
|
||||
|
||||
/**
|
||||
* 联系我们页面组件。
|
||||
* 优化后的版本,移除了联系表单,专注于清晰地展示联系方式。
|
||||
* 采用单栏居中布局,设计简洁、现代。
|
||||
*/
|
||||
const ContactUsPage = async ({ params }: { params: { lang: string } }) => {
|
||||
// 1. 将 props 类型更正为普通对象
|
||||
const ContactUsPage: React.FC<{ params: paramsType}> = async ({ params }) => {
|
||||
// 2. 直接从 params 对象中解构 lang,移除 use()
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'contact');
|
||||
const { t } = await getTranslation(lang, 'contact');
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 min-h-screen py-12 sm:py-16">
|
||||
|
||||
@@ -4,7 +4,7 @@ import './globals.css';
|
||||
import FooterLinks from '../../components/FooterLinks';
|
||||
import { dir } from 'i18next';
|
||||
import { languages } from '../../i18n/settings';
|
||||
import { useTranslation } from '../../i18n';
|
||||
import { getTranslation } from '../../i18n';
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../lib/utils';
|
||||
|
||||
@@ -17,7 +17,7 @@ const inter = Inter({
|
||||
|
||||
export async function generateMetadata({ params }: { params: { lang: string } }): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'layout');
|
||||
const { t } = await getTranslation(lang, 'layout');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
return {
|
||||
metadataBase: new URL('https://www.podcasthub.com'),
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Metadata } from 'next';
|
||||
import PodcastContent from '@/components/PodcastContent';
|
||||
import { useTranslation } from '../../../../i18n'; // 导入 useTranslation
|
||||
import { getTranslation } from '../../../../i18n'; // 导入 getTranslation
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../../../lib/utils';
|
||||
|
||||
export async function generateMetadata({ params }: PodcastDetailPageProps): Promise<Metadata> {
|
||||
export type paramsType = Promise<{ lang: string, fileName: string }>;
|
||||
|
||||
export async function generateMetadata({ params }: { params: paramsType }): Promise<Metadata> {
|
||||
const { fileName, lang } = await params;
|
||||
const { t } = await useTranslation(lang);
|
||||
const { t } = await getTranslation(lang);
|
||||
const decodedFileName = decodeURIComponent(fileName);
|
||||
const title = `${t('podcastContent.podcastDetails')} - ${decodedFileName}`;
|
||||
const description = `${t('podcastContent.listenToPodcast')} ${decodedFileName}。`;
|
||||
@@ -20,18 +22,13 @@ export async function generateMetadata({ params }: PodcastDetailPageProps): Prom
|
||||
};
|
||||
}
|
||||
|
||||
interface PodcastDetailPageProps {
|
||||
params: {
|
||||
fileName: string;
|
||||
lang: string; // 添加 lang 属性
|
||||
};
|
||||
}
|
||||
|
||||
export default async function PodcastDetailPage({ params }: PodcastDetailPageProps) {
|
||||
const PodcastDetailPage: React.FC<{ params: paramsType}> = async ({ params }) => {
|
||||
const { fileName, lang } = await params; // 解构 lang
|
||||
return (
|
||||
<div className="bg-white text-gray-800 font-sans">
|
||||
<PodcastContent fileName={decodeURIComponent(fileName)} lang={lang} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PodcastDetailPage;
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Metadata } from 'next';
|
||||
import React, { use } from 'react';
|
||||
import PricingSection from '@/components/PricingSection'; // 导入 PricingSection 组件
|
||||
import { useTranslation } from '../../../i18n';
|
||||
import { getTranslation } from '../../../i18n';
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../../lib/utils';
|
||||
|
||||
export async function generateMetadata({ params }: { params: { lang: string } }): Promise<Metadata> {
|
||||
export type paramsType = Promise<{ lang: string }>;
|
||||
|
||||
export async function generateMetadata({ params }: { params: paramsType }): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const { t } = await (await import('../../../i18n')).useTranslation(lang, 'components');
|
||||
const { t } = await (await import('../../../i18n')).getTranslation(lang, 'components');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
return {
|
||||
title: t('pricing_page_title'),
|
||||
@@ -18,10 +20,10 @@ export async function generateMetadata({ params }: { params: { lang: string } })
|
||||
};
|
||||
}
|
||||
|
||||
const PricingPage = async ({ params }: { params: { lang: string } }) => {
|
||||
const PricingPage: React.FC<{ params: paramsType}> = async ({ params }) => {
|
||||
const { lang } = await params;
|
||||
// 尽管 PricingSection 是客户端组件,为了使 PricingPage 成为服务器组件并加载服务端 i18n,我们在这里模拟加载
|
||||
await useTranslation(lang, 'components');
|
||||
await getTranslation(lang, 'components');
|
||||
return (
|
||||
<PricingSection lang={lang} />
|
||||
);
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Metadata } from 'next';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../../lib/utils';
|
||||
|
||||
export type paramsType = Promise<{ lang: string }>;
|
||||
/**
|
||||
* 设置页面元数据。
|
||||
*/
|
||||
export async function generateMetadata({ params }: { params: { lang: string } }): Promise<Metadata> {
|
||||
export async function generateMetadata({ params }: { params: paramsType }): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'privacy');
|
||||
const { t } = await getTranslation(lang, 'privacy');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
return {
|
||||
title: t('privacy_policy.title'),
|
||||
@@ -20,20 +21,9 @@ export async function generateMetadata({ params }: { params: { lang: string } })
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐私政策页面组件。
|
||||
* 提供了详细的隐私政策说明,涵盖信息收集、使用、共享、安全及用户权利。
|
||||
* 布局采用 Tailwind CSS 进行优化,`prose` 类用于美化排版,`break-words` 确保内容不会溢出容器。
|
||||
*/
|
||||
type PrivacyPolicyPageProps = {
|
||||
params: {
|
||||
lang: string;
|
||||
};
|
||||
};
|
||||
|
||||
const PrivacyPolicyPage: React.FC<PrivacyPolicyPageProps> = async ({ params }) => {
|
||||
const PrivacyPolicyPage: React.FC<{ params: paramsType}> = async ({ params }) => {
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'privacy');
|
||||
const { t } = await getTranslation(lang, 'privacy');
|
||||
return (
|
||||
<div className="bg-gray-50 min-h-screen py-12 sm:py-16">
|
||||
<div className="container mx-auto p-6 md:p-8 max-w-4xl bg-white shadow-lg rounded-lg">
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React from 'react';
|
||||
import { Metadata } from 'next';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { languages } from '@/i18n/settings';
|
||||
import { headers } from 'next/headers';
|
||||
import { getTruePathFromHeaders } from '../../../lib/utils';
|
||||
|
||||
export async function generateMetadata({ params }: { params: { lang: string } }): Promise<Metadata> {
|
||||
export type paramsType = Promise<{ lang: string }>;
|
||||
|
||||
export async function generateMetadata({ params }: { params: paramsType }): Promise<Metadata> {
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'terms');
|
||||
const { t } = await getTranslation(lang, 'terms');
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
return {
|
||||
title: t('terms_of_service.title'),
|
||||
@@ -18,9 +20,9 @@ export async function generateMetadata({ params }: { params: { lang: string } })
|
||||
};
|
||||
}
|
||||
|
||||
const TermsOfServicePage: React.FC<{ params: { lang: string } }> = async ({ params }) => {
|
||||
const TermsOfServicePage: React.FC<{ params: paramsType }> = async ({ params }) => {
|
||||
const { lang } = await params;
|
||||
const { t } = await useTranslation(lang, 'terms');
|
||||
const { t } = await getTranslation(lang, 'terms');
|
||||
return (
|
||||
<div className="bg-gray-50 min-h-screen py-12 sm:py-16">
|
||||
<div className="container mx-auto p-6 md:p-8 max-w-4xl bg-white shadow-lg rounded-lg">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { fetchAndCacheProvidersLocal } from '@/lib/config-local';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
|
||||
// 获取查询参数
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getAudioInfo, getUserInfo } from '@/lib/podcastApi';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getLanguageFromRequest } from '@/lib/utils';
|
||||
*/
|
||||
export async function GET(req: NextRequest) {
|
||||
const lang = getLanguageFromRequest(req);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
// 从请求 URL 中获取查询参数
|
||||
const { searchParams } = new URL(req.url);
|
||||
const fileName = searchParams.get('file_name');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
const filename = request.nextUrl.searchParams.get('filename');
|
||||
|
||||
// 验证文件名安全性
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import type { TTSConfig } from '@/types';
|
||||
import { useTranslation } from '@/i18n'; // 导入 useTranslation
|
||||
import { getTranslation } from '@/i18n'; // 导入 getTranslation
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
// 缓存对象,存储响应数据和时间戳
|
||||
@@ -38,7 +38,7 @@ const TTS_PROVIDER_ORDER = [
|
||||
// 获取配置文件列表
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors'); // 加载 'errors' 命名空间的翻译
|
||||
const { t } = await getTranslation(lang, 'errors'); // 加载 'errors' 命名空间的翻译
|
||||
|
||||
const cacheKey = `config_files_list_${lang}`; // 缓存键中包含语言
|
||||
const cachedData = getCache(cacheKey);
|
||||
@@ -97,7 +97,7 @@ export async function GET(request: NextRequest) {
|
||||
// 获取特定配置文件内容
|
||||
export async function POST(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors'); // 加载 'errors' 命名空间的翻译
|
||||
const { t } = await getTranslation(lang, 'errors'); // 加载 'errors' 命名空间的翻译
|
||||
|
||||
const { configFile } = await request.json();
|
||||
const cacheKey = `config_file_${configFile}_${lang}`; // 缓存键中包含语言
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { PodcastGenerationRequest } from '@/types'; // 导入 SettingsFormD
|
||||
import { getSessionData } from '@/lib/server-actions';
|
||||
import { getUserPoints } from '@/lib/points'; // 导入 getUserPoints
|
||||
import { fetchAndCacheProvidersLocal } from '@/lib/config-local'; // 导入 getTTSProviders
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const enableTTSConfigPage = process.env.NEXT_PUBLIC_ENABLE_TTS_CONFIG_PAGE === '
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
|
||||
const session = await getSessionData();
|
||||
const userId = session.user?.id;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getPodcastStatus } from '@/lib/podcastApi';
|
||||
import { getSessionData } from '@/lib/server-actions';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
export const revalidate = 0; // 等同于 `cache: 'no-store'`
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
|
||||
const session = await getSessionData();
|
||||
const userId = session.user?.id;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { getUserPoints, deductUserPoints, addPointsToUser, hasUserSignedToday } from "@/lib/points"; // 导入 deductUserPoints, addPointsToUser, hasUserSignedToday
|
||||
import { NextResponse, NextRequest } from "next/server"; // 导入 NextRequest
|
||||
import { getSessionData } from "@/lib/server-actions"; // 导入 getSessionData
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils'; // 导入 getLanguageFromRequest
|
||||
|
||||
export async function GET(request: NextRequest) { // GET 函数接收 request
|
||||
const session = await getSessionData(); // 使用 getSessionData 获取 session
|
||||
const lang = getLanguageFromRequest(request); // 获取语言
|
||||
const { t } = await useTranslation(lang, 'errors'); // 初始化翻译
|
||||
const { t } = await getTranslation(lang, 'errors'); // 初始化翻译
|
||||
if (!session || !session.user || !session.user.id) {
|
||||
return NextResponse.json({ success: false, error: t("unauthorized") }, { status: 401 });
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export async function GET(request: NextRequest) { // GET 函数接收 request
|
||||
export async function PUT(request: NextRequest) {
|
||||
const { task_id, auth_id, timestamp, status } = await request.json();
|
||||
const lang = getLanguageFromRequest(request); // 获取语言
|
||||
const { t } = await useTranslation(lang, 'errors'); // 初始化翻译
|
||||
const { t } = await getTranslation(lang, 'errors'); // 初始化翻译
|
||||
try {
|
||||
if(status !== 'completed') {
|
||||
return NextResponse.json({ success: false, error: t("invalid_status") }, { status: 400 });
|
||||
@@ -89,7 +89,7 @@ export async function POST(request: NextRequest) {
|
||||
const session = await getSessionData();
|
||||
const lang = getLanguageFromRequest(request); // 获取语言
|
||||
console.log(lang)
|
||||
const { t } = await useTranslation(lang, 'errors'); // 初始化翻译
|
||||
const { t } = await getTranslation(lang, 'errors'); // 初始化翻译
|
||||
if (!session || !session.user || !session.user.id) {
|
||||
return NextResponse.json({ success: false, error: t("unauthorized") }, { status: 401 });
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { getUserPointsTransactions } from "@/lib/points";
|
||||
import { NextResponse, NextRequest } from "next/server";
|
||||
import { getSessionData } from "@/lib/server-actions";
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
|
||||
const session = await getSessionData();
|
||||
if (!session || !session.user || !session.user.id) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { fetchAndCacheProvidersLocal } from '@/lib/config-local';
|
||||
import { useTranslation } from '@/i18n';
|
||||
import { getTranslation } from '@/i18n';
|
||||
import { getLanguageFromRequest } from '@/lib/utils';
|
||||
|
||||
|
||||
// 获取 tts_providers.json 文件内容
|
||||
export async function GET(request: NextRequest) {
|
||||
const lang = getLanguageFromRequest(request);
|
||||
const { t } = await useTranslation(lang, 'errors');
|
||||
const { t } = await getTranslation(lang, 'errors');
|
||||
|
||||
try {
|
||||
const config = await fetchAndCacheProvidersLocal(lang);
|
||||
|
||||
@@ -111,7 +111,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({
|
||||
audio.removeEventListener('loadstart', handleLoadStart);
|
||||
audio.removeEventListener('canplay', handleCanPlay);
|
||||
};
|
||||
}, [isPlaying, podcast.audioUrl, canPlay]); // 将 canPlay 加入依赖,确保状态变化时触发播放
|
||||
}, [isPlaying, podcast.audioUrl, canPlay, onEnded, currentPlaybackRate]); // 添加 onEnded 和 currentPlaybackRate
|
||||
|
||||
// 当播客URL变化时,更新audio元素的src
|
||||
useEffect(() => {
|
||||
|
||||
@@ -56,7 +56,7 @@ const AudioVisualizer: React.FC<AudioVisualizerProps> = ({
|
||||
audioContext.close();
|
||||
}
|
||||
};
|
||||
}, [audioElement]);
|
||||
}, [audioElement, audioContext]); // 添加 audioContext
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPlaying || !analyserRef.current || !dataArrayRef.current || !canvasRef.current) {
|
||||
|
||||
@@ -133,7 +133,7 @@ const ConfigSelector: React.FC<ConfigSelectorProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
loadConfigFiles();
|
||||
}, []);
|
||||
}, [loadConfigFiles]); // 添加 loadConfigFiles
|
||||
|
||||
// 监听localStorage变化,重新加载配置
|
||||
useEffect(() => {
|
||||
@@ -151,7 +151,7 @@ const ConfigSelector: React.FC<ConfigSelectorProps> = ({
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
window.removeEventListener('settingsUpdated', handleStorageChange);
|
||||
};
|
||||
}, [selectedConfig]);
|
||||
}, [selectedConfig, loadConfigFiles]); // 添加 loadConfigFiles
|
||||
|
||||
const handleConfigSelect = (configFile: string) => {
|
||||
setSelectedConfig(configFile);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getAudioInfo, getUserInfo } from '@/lib/podcastApi';
|
||||
import AudioPlayerControls from './AudioPlayerControls';
|
||||
import PodcastTabs from './PodcastTabs';
|
||||
import ShareButton from './ShareButton'; // 导入 ShareButton 组件
|
||||
import { useTranslation } from '../i18n'; // 从正确路径导入 useTranslation
|
||||
import { getTranslation } from '../i18n'; // 从正确路径导入 useTranslation
|
||||
import { headers } from 'next/headers'; // 导入 usePathname
|
||||
import { getTruePathFromHeaders } from '../lib/utils'; // 导入新函数
|
||||
|
||||
@@ -33,7 +33,7 @@ interface PodcastContentProps {
|
||||
|
||||
|
||||
export default async function PodcastContent({ fileName, lang }: PodcastContentProps) {
|
||||
const { t } = await useTranslation(lang, 'components'); // 初始化 useTranslation
|
||||
const { t } = await getTranslation(lang, 'components'); // 初始化 getTranslation
|
||||
const result = await getAudioInfo(fileName, lang);
|
||||
const truePath = await getTruePathFromHeaders(await headers(), lang);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const initI18next = async (lng: string, ns: string | string[] | undefined): Prom
|
||||
return i18nInstance
|
||||
}
|
||||
|
||||
export async function useTranslation(lng: string, ns: string | string[] | undefined = 'common', options: { keyPrefix?: string } = {}) {
|
||||
export async function getTranslation(lng: string, ns: string | string[] | undefined = 'common', options: { keyPrefix?: string } = {}) {
|
||||
const i18nextInstance = await initI18next(lng, ns)
|
||||
return {
|
||||
t: i18nextInstance.getFixedT(lng, Array.isArray(ns) ? ns[0] : ns, options.keyPrefix),
|
||||
|
||||
Reference in New Issue
Block a user