diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..e694077 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +module.exports = { + preset: 'ts-jest', + transform: { '^.+\\.ts?$': 'ts-jest' }, + testEnvironment: 'node', + testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + roots: ['/src'], + moduleNameMapper: { + '@/(.*)': '/src/$1' + } +} diff --git a/package.json b/package.json index 814a0af..2727ad4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dev": "wrangler dev src/index.ts --env=local", "build": "wrangler build src/index.ts", "deploy": "wrangler deploy --minify src/index.ts", + "test": "jest", "prettier": "prettier . --write" }, "dependencies": { @@ -14,8 +15,11 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20230914.0", + "@types/jest": "^29.5.12", "@types/react": "^18.2.46", + "jest": "^29.7.0", "prettier": "3.2.5", + "ts-jest": "^29.1.3", "wrangler": "^3.21.0" }, "homepage": "https://tnktok.com" diff --git a/src/index.ts b/src/index.ts index f7a7ae9..780c4c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,18 +11,24 @@ const app = new Hono() app.get('/test/:videoId', async (c) => { const { videoId } = c.req.param() - const awemeId = await scrapeVideoData(videoId) - if (awemeId instanceof Error) { - return new Response((awemeId as Error).message, { status: 500 }) - } + try { + const videoData = await scrapeVideoData(videoId) - return new Response(JSON.stringify(awemeId), { - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - } - }) + return new Response(JSON.stringify(videoData), { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + }) + } catch (e) { + return new Response((e as Error).message, { + status: 500, + headers: { + 'Content-Type': 'text/plain; charset=utf-8' + } + }) + } }) app.get('/', (c) => { @@ -59,7 +65,7 @@ async function handleVideo(c: any): Promise { id = awemeId } catch (e) { const responseContent = await ErrorResponse((e as Error).message) - return returnHTMLResponse(responseContent, 201) + return returnHTMLResponse(responseContent, 500) } } @@ -68,7 +74,7 @@ async function handleVideo(c: any): Promise { if (videoInfo instanceof Error) { const responseContent = await ErrorResponse((videoInfo as Error).message) - return returnHTMLResponse(responseContent, 201) + return returnHTMLResponse(responseContent, 500) } const url = new URL(c.req.url) @@ -99,9 +105,8 @@ async function handleVideo(c: any): Promise { return returnHTMLResponse(responseContent) } } catch (e) { - console.log(e) const responseContent = await ErrorResponse((e as Error).message) - return returnHTMLResponse(responseContent, 201) + return returnHTMLResponse(responseContent, 500) } } @@ -116,21 +121,13 @@ app.get('/generate/alternate', (c) => { }) }) -app.get( - '/generate/*', - cache({ - cacheName: 'my-app', - cacheControl: 'max-age=3600' - }) -) - app.get('/generate/video/:videoId', async (c) => { const { videoId } = c.req.param() try { - /* - const data = await scrapeVideoData(videoId); + const data = await scrapeVideoData(videoId) + /* if (!(data instanceof Error)) { if(data.video.playAddr) { return c.redirect(data.video.playAddr) @@ -143,6 +140,7 @@ app.get('/generate/video/:videoId', async (c) => { } } */ + return c.redirect(`https://tikwm.com/video/media/play/${videoId}.mp4`) } catch (e) { return new Response((e as Error).message, { @@ -158,9 +156,9 @@ app.get('/generate/image/:videoId', async (c) => { const { videoId } = c.req.param() try { - /* - const data = await scrapeVideoData(videoId); + const data = await scrapeVideoData(videoId) + /* if (!(data instanceof Error)) { if(data.imagePost.images.length > 0) { return c.redirect(data.imagePost.images[0].imageURL.urlList[0]) @@ -169,6 +167,7 @@ app.get('/generate/image/:videoId', async (c) => { } } */ + return c.redirect(`https://tikwm.com/video/cover/${videoId}.webp`) } catch (e) { return new Response((e as Error).message, { diff --git a/src/services/tiktok.ts b/src/services/tiktok.ts index 467a420..b20cf89 100644 --- a/src/services/tiktok.ts +++ b/src/services/tiktok.ts @@ -20,8 +20,7 @@ export async function grabAwemeId(videoId: string): Promise { } export async function scrapeVideoData(awemeId: string, author?: string): Promise { - console.log('before', cookie.getUpdatingCookies()) - const res = await fetch(`https://www.tiktok.com/@${author || 'i'}"/video/${awemeId}`, { + const res = await fetch(`https://www.tiktok.com/@${author || 'i'}/video/${awemeId}`, { method: 'GET', headers: { Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', @@ -34,8 +33,6 @@ export async function scrapeVideoData(awemeId: string, author?: string): Promise } }) - console.log('string', cookie.getCookiesAsString()) - console.log(res.headers) let cookies = cookieParser(res.headers.get('set-cookie')!) cookie.setCookies(cookies) @@ -47,18 +44,15 @@ export async function scrapeVideoData(awemeId: string, author?: string): Promise .split('')[0] const json: WebJSONResponse = JSON.parse(resJson) - //console.log(Object.keys(json["__DEFAULT_SCOPE__"])); if ( !json['__DEFAULT_SCOPE__']['webapp.video-detail'] || json['__DEFAULT_SCOPE__']['webapp.video-detail'].statusCode == 10204 ) throw new Error('Could not find video data') const videoInfo = json['__DEFAULT_SCOPE__']['webapp.video-detail']['itemInfo']['itemStruct'] - //console.log(videoInfo) return videoInfo } catch (err) { - console.log(err) throw new Error('Could not parse video info') } } diff --git a/src/services/tiktokv.ts b/src/services/tiktokv.ts index f7d9d98..abcc215 100644 --- a/src/services/tiktokv.ts +++ b/src/services/tiktokv.ts @@ -27,7 +27,7 @@ export async function getVideoInfo(awemeId: string): Promise cacheTtlByStatus: { '200-299': 86400, 404: 1, '500-599': 0 } } }) - const json: TikTokAPIResponse = await res.json() + const json = (await res.json()) as TikTokAPIResponse const videoInfo: AwemeList | undefined = json.aweme_list.find((aweme) => aweme.aweme_id === awemeId) if (videoInfo) { diff --git a/src/templates/pages/VideoResponse.tsx b/src/templates/pages/VideoResponse.tsx index e902a3e..31f59b5 100644 --- a/src/templates/pages/VideoResponse.tsx +++ b/src/templates/pages/VideoResponse.tsx @@ -22,7 +22,6 @@ export function VideoResponse(data: ItemStruct): JSX.Element { } */ - console.log(data) return ( <> {MetaHelper( diff --git a/src/tests/main.test.ts b/src/tests/main.test.ts new file mode 100644 index 0000000..ad79b75 --- /dev/null +++ b/src/tests/main.test.ts @@ -0,0 +1,20 @@ +import app from '@/index' + +describe('GET /test/:videoId', () => { + it('should return 200', async () => { + const res = await app.request('/test/7332187682480590112', { + method: 'GET' + }) + + expect(res.status).toBe(200) + }) + + it('should return 500', async () => { + const res = await app.request('/test/123', { + method: 'GET' + }) + + expect(res.status).toBe(500) + expect(await res.text()).toBe('Could not parse video info') + }) +}) diff --git a/src/tests/photo.test.ts b/src/tests/photo.test.ts new file mode 100644 index 0000000..4695ea0 --- /dev/null +++ b/src/tests/photo.test.ts @@ -0,0 +1,53 @@ +import app from '@/index' + +describe('GET /@i/photo/:videoId', () => { + it('should return 200', async () => { + const res = await app.request('/@i/photo/7335753580093164833', { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)' + } + }) + + expect(res.status).toBe(200) + }) + + // no discord user agent, redirects + it('should return 302', async () => { + const res = await app.request('/@i/photo/7335753580093164833', { + method: 'GET' + }) + + expect(res.status).toBe(302) + }) + + it('should return 500', async () => { + const res = await app.request('/@i/photo/123', { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)' + } + }) + + expect(res.status).toBe(500) + expect(await res.text()).toContain('An error occurred while trying to fetch the video. Please try again later.') + }) +}) + +describe('GET /generate/image/:videoId', () => { + it('should return 302', async () => { + const res = await app.request('/generate/image/7335753580093164833', { + method: 'GET' + }) + + expect(res.status).toBe(302) + }) + + it('should return 500', async () => { + const res = await app.request('/generate/image/123', { + method: 'GET' + }) + + expect(res.status).toBe(500) + }) +}) diff --git a/src/tests/video.test.ts b/src/tests/video.test.ts new file mode 100644 index 0000000..9117fa2 --- /dev/null +++ b/src/tests/video.test.ts @@ -0,0 +1,66 @@ +import app from '@/index' + +describe('GET /@i/video/:videoId', () => { + it('should return 200', async () => { + const res = await app.request('/@pr4yforgabs/video/7332187682480590112', { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)' + } + }) + + expect(res.status).toBe(200) + }) + + // no discord user agent, redirects + it('should return 302', async () => { + const res = await app.request('/@pr4yforgabs/video/7332187682480590112', { + method: 'GET' + }) + + expect(res.status).toBe(302) + }) + + it('should return 500', async () => { + const res = await app.request('/@i/video/123', { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)' + } + }) + + expect(res.status).toBe(500) + expect(await res.text()).toContain('An error occurred while trying to fetch the video. Please try again later.') + }) +}) + +describe('GET /t/:videoId', () => { + it('should return 200', async () => { + const res = await app.request('/t/ZPRKrbUB1', { + method: 'GET', + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)' + } + }) + + expect(res.status).toBe(200) + }) +}) + +describe('GET /generate/video/:videoId', () => { + it('should return 302', async () => { + const res = await app.request('/generate/video/7332187682480590112', { + method: 'GET' + }) + + expect(res.status).toBe(302) + }) + + it('should return 500', async () => { + const res = await app.request('/generate/video/123', { + method: 'GET' + }) + + expect(res.status).toBe(500) + }) +}) diff --git a/src/util/cookieHelper.ts b/src/util/cookieHelper.ts index 4db5716..4656b43 100644 --- a/src/util/cookieHelper.ts +++ b/src/util/cookieHelper.ts @@ -24,26 +24,8 @@ export default class CookieHelper { return Object.freeze({ ...this.cookies }) } getCookiesAsString() { - /* - this.cookies - [ - { - name: 'ttwid', - value: '1|8WN5g_4UuJAXRCCDkpvHVs1VcAxDbE1YbchrWF9WLpQ|1716658463|7e41f1aa8f1a95fa8a4d5e4e56dc24eb6c05bbff5e66db010e6cd7f543a5a463', - domain: '.tiktok.com', - path: '/', - expires: 2024-11-21T17:34:23.000Z, - 'httponly, tt_csrf_token': '3fBpsXXj-X7xQXEbXEchtyGe7ZxGbSzQGJHo', - sameSite: 'lax', - secure: true, - 'httponly, tt_chain_token': 'ATA0mqdlbe36/vayBw/R2g==', - httpOnly: true - } -] - */ return this.cookies .map((cookie) => { - console.log(cookie) return `${cookie.name}=${cookie.value}` }) .join('; ') diff --git a/tsconfig.json b/tsconfig.json index 4a2abdb..9b9d53d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,8 +6,11 @@ "esModuleInterop": true, "strict": true, "lib": ["esnext"], - "types": ["@cloudflare/workers-types"], + "types": ["@cloudflare/workers-types", "@types/jest"], "jsx": "react-jsx", - "jsxImportSource": "hono/jsx" + "jsxImportSource": "hono/jsx", + "paths": { + "@/*": ["./src/*"] + } } }