├── .env ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── README.md ├── __test__ ├── __snapshots__ │ └── video-list.test.tsx.snap ├── app.test.tsx ├── test.test.ts ├── useOnScreen.test.tsx └── video-list.test.tsx ├── components ├── login-btn.tsx └── video-list.tsx ├── hooks └── useOnScreen.ts ├── lib ├── prisma.ts ├── sendVerificationRequest.ts └── util.ts ├── next.config.js ├── package.json ├── pages ├── _app.js ├── api │ ├── auth │ │ └── [...nextauth].ts │ ├── chapter.ts │ └── hello.js ├── auth │ └── login.tsx ├── index.tsx ├── me.tsx └── video │ ├── [id].tsx │ └── chapter │ └── [id].tsx ├── postcss.config.js ├── prisma ├── example.json ├── example2.json ├── migrations │ ├── 20221004064203_init │ │ └── migration.sql │ ├── 20221004072200_add_cover │ │ └── migration.sql │ ├── 20221012072321_user │ │ └── migration.sql │ └── migration_lock.toml ├── schema.prisma └── seed.ts ├── public ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types └── next-auth.d.ts ├── vitest-setup.ts ├── vitest.config.ts └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="postgresql://test:example@localhost:5432/db_video?schema=public" 8 | 9 | GITHUB_ID=e1a1fc5f64bd82a965a8 10 | GITHUB_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 11 | 12 | EMAIL_SERVER=smtps://maqi1520@163.com:xxxxxxxxxxxxxxxxxx@smtp.163.com:465 13 | EMAIL_FROM=狂奔的小马 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /__test__/__snapshots__/video-list.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1 2 | 3 | exports[`VideoList > it should be render 1`] = ` 4 |
5 |
6 |
7 | `; 8 | -------------------------------------------------------------------------------- /__test__/app.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { render, screen } from "@testing-library/react"; 3 | 4 | function App() { 5 | return
app
; 6 | } 7 | 8 | describe("App", () => { 9 | it("it should be render", () => { 10 | render(); 11 | expect(screen.getByText("app")).toBeInTheDocument(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /__test__/test.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | 3 | describe("something truthy and falsy", () => { 4 | it("true to be true", () => { 5 | expect(true).toBe(true); 6 | }); 7 | 8 | it("false to be false", () => { 9 | expect(false).toBe(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /__test__/useOnScreen.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it, vi } from "vitest"; 2 | import { render, screen, renderHook, act } from "@testing-library/react"; 3 | import useOnScreen from "@/hooks/useOnScreen"; 4 | import { useRef } from "react"; 5 | 6 | const IntersectionObserverMock = vi.fn((a) => ({ 7 | disconnect: vi.fn(), 8 | observe: vi.fn(), 9 | takeRecords: vi.fn(), 10 | unobserve: vi.fn(), 11 | })); 12 | 13 | vi.stubGlobal("IntersectionObserver", IntersectionObserverMock); 14 | 15 | function App() { 16 | const ref = useRef(); 17 | const visible = useOnScreen(ref); 18 | return ( 19 |
20 | {visible ? "true" : "false"} 21 |
22 | ); 23 | } 24 | 25 | describe("useOnScreen", () => { 26 | it("default value is false,After callback value should be true", async () => { 27 | render(); 28 | 29 | const callback = IntersectionObserverMock.mock.calls[0][0]; 30 | 31 | expect(screen.getByRole("test")).toHaveTextContent("false"); 32 | 33 | act(() => { 34 | callback([{ isIntersecting: true }]); 35 | }); 36 | expect(screen.getByRole("test")).toHaveTextContent("true"); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /__test__/video-list.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { render, screen } from "@testing-library/react"; 3 | import VideoList from "@/components/video-list"; 4 | 5 | const mockData = [ 6 | { 7 | id: 1, 8 | title: "高中数学:代数基础", 9 | desc: "代数是既美丽有重要的学习领域,拥有着无限的应用可能。你可以花上一辈子的时间来研究和探索它(真的有人这样做了)。如果你不是他们中的一员,只是想学习一下,了解和联系代数的最核心概念,这里你来对了。想要准备高中或者大学考试的朋友,这里对你们很理想。这里覆盖了代数和相关代数预科、几何的基础概念。如果你想看更多的内容,还可以去看看代数1和代数2。", 10 | pic: "https://gcdp.oss-cn-qingdao.aliyuncs.com/201602/18/1455776864827_2156.jpg", 11 | authorId: "cl95b4ny10000fjnuhm7rvys5", 12 | categoryId: 1, 13 | level: 1, 14 | createdAt: new Date(), 15 | updatedAt: new Date(), 16 | author: { 17 | id: "cl95b4ny10000fjnuhm7rvys5", 18 | name: "maqibin", 19 | email: "164377467@qq.com", 20 | emailVerified: new Date(), 21 | image: "https://avatars.githubusercontent.com/u/9312044?v=4", 22 | }, 23 | }, 24 | ]; 25 | 26 | describe("VideoList", () => { 27 | it("it should be render", () => { 28 | const { container } = render(); 29 | 30 | expect(container).toMatchSnapshot(); 31 | }); 32 | 33 | it("it should be render with data", () => { 34 | render(); 35 | 36 | mockData.forEach((item) => { 37 | expect(screen.getAllByText(item.title)).toBeDefined(); 38 | expect(screen.getAllByAltText(item.title)).toBeDefined(); 39 | expect(screen.getAllByText(item.author.name)).toBeDefined(); 40 | }); 41 | }); 42 | 43 | it("it should be render with className", () => { 44 | const mockClassName = "mockClassName"; 45 | const { container } = render( 46 | 47 | ); 48 | 49 | expect(container.getElementsByClassName(mockClassName).length).toBe(1); 50 | }); 51 | 52 | it("it should be editable with editable props", () => { 53 | const { container } = render(); 54 | 55 | expect(screen.queryAllByRole("edit")).toHaveLength(mockData.length); 56 | expect(screen.queryAllByRole("delete")).toHaveLength(mockData.length); 57 | }); 58 | 59 | it("it should be has 'test-vertical' class when horizontal is false ", () => { 60 | const { container } = render( 61 | 62 | ); 63 | 64 | expect(container.getElementsByClassName("test-vertical")).toHaveLength( 65 | mockData.length 66 | ); 67 | }); 68 | 69 | it("it should be has 'test-horizontal' class when horizontal is false ", () => { 70 | const { container } = render(); 71 | 72 | expect(container.getElementsByClassName("test-horizontal")).toHaveLength( 73 | mockData.length 74 | ); 75 | }); 76 | 77 | it("it should not be editable without editable props", () => { 78 | render(); 79 | 80 | expect(screen.queryByRole("edit")).toBeNull(); 81 | expect(screen.queryByRole("delete")).toBeNull(); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /components/login-btn.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @next/next/no-img-element */ 2 | import { useSession, signIn, signOut } from "next-auth/react"; 3 | import Link from "next/link"; 4 | 5 | export default function Component() { 6 | const { data: session } = useSession(); 7 | 8 | if (session) { 9 | return ( 10 |
11 | avator 16 | 17 | {session.user.name} 18 | 19 | 25 |
26 | ); 27 | } 28 | return ( 29 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /components/video-list.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "next/link"; 3 | import Image from "next/image"; 4 | import { Video, User } from "@prisma/client"; 5 | 6 | type Props = { 7 | className?: string; 8 | editable?: boolean; 9 | horizontal?: boolean; 10 | data: (Video & { 11 | author: User; 12 | })[]; 13 | }; 14 | 15 | type ItemProps = { 16 | editable?: boolean; 17 | horizontal?: boolean; 18 | item: Video & { 19 | author: User; 20 | }; 21 | }; 22 | 23 | function VideoItem({ item, horizontal, editable }: ItemProps) { 24 | return ( 25 |
29 | 46 | 47 | {editable && ( 48 |
49 | 55 | 61 |
62 | )} 63 |
64 | ); 65 | } 66 | 67 | export default function VideoList({ 68 | data, 69 | editable, 70 | className, 71 | horizontal, 72 | }: Props) { 73 | return ( 74 |
75 | {data.map((item) => { 76 | return ( 77 | 83 | ); 84 | })} 85 |
86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /hooks/useOnScreen.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, MutableRefObject } from "react"; 2 | 3 | export default function useOnScreen( 4 | ref: MutableRefObject, 5 | rootMargin: string = "0px" 6 | ): boolean { 7 | // 元素是否可见 8 | const [isIntersecting, setIntersecting] = useState(false); 9 | useEffect(() => { 10 | const observer = new IntersectionObserver( 11 | ([entry]) => { 12 | //更新返回数据 13 | setIntersecting(entry.isIntersecting); 14 | }, 15 | { 16 | rootMargin, 17 | } 18 | ); 19 | if (ref.current) { 20 | observer.observe(ref.current); 21 | } 22 | return () => { 23 | observer.unobserve(ref.current); 24 | }; 25 | }, []); //只在挂载的时候监听一次 26 | return isIntersecting; 27 | } 28 | -------------------------------------------------------------------------------- /lib/prisma.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | // PrismaClient is attached to the `global` object in development to prevent 4 | // exhausting your database connection limit. 5 | // 6 | // Learn more: 7 | // https://pris.ly/d/help/next-js-best-practices 8 | 9 | let prisma: PrismaClient; 10 | 11 | if (process.env.NODE_ENV === "production") { 12 | prisma = new PrismaClient(); 13 | } else { 14 | if (!global.prisma) { 15 | global.prisma = new PrismaClient(); 16 | } 17 | prisma = global.prisma; 18 | } 19 | export default prisma; 20 | -------------------------------------------------------------------------------- /lib/sendVerificationRequest.ts: -------------------------------------------------------------------------------- 1 | import { createTransport } from "nodemailer"; 2 | import { SendVerificationRequestParams } from "next-auth/providers/email"; 3 | import { Theme } from "next-auth"; 4 | 5 | export async function sendVerificationRequest( 6 | params: SendVerificationRequestParams 7 | ) { 8 | const { identifier, url, provider, theme } = params; 9 | const { host } = new URL(url); 10 | const transport = createTransport(provider.server); 11 | const result = await transport.sendMail({ 12 | to: identifier, 13 | from: provider.from, 14 | subject: `${host} 注册认证`, 15 | text: text({ url, host }), 16 | html: html({ url, host, theme }), 17 | }); 18 | const failed = result.rejected.concat(result.pending).filter(Boolean); 19 | if (failed.length) { 20 | throw new Error(`Email(s) (${failed.join(", ")}) could not be sent`); 21 | } 22 | } 23 | 24 | /** 25 | *使用HTML body 代替正文内容 26 | */ 27 | function html(params: { url: string; host: string; theme: Theme }) { 28 | const { url, host, theme } = params; 29 | //由于使用 30 | const escapedHost = host.replace(/\./g, "​."); 31 | 32 | return ` 33 | 34 |
欢迎注册${escapedHost},点击登录
35 | 36 | `; 37 | } 38 | 39 | /** 不支持HTML 的邮件客户端会显示下面的文本信息 */ 40 | function text({ url, host }: { url: string; host: string }) { 41 | return `欢迎注册 ${host}\n点击${url}登录\n\n`; 42 | } 43 | -------------------------------------------------------------------------------- /lib/util.ts: -------------------------------------------------------------------------------- 1 | export function makeSerializable(o: T): T { 2 | return JSON.parse(JSON.stringify(o)); 3 | } 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | images: { 6 | domains: [ 7 | "gcdp.oss-cn-qingdao.aliyuncs.com", 8 | "yxg-image.oss-cn-qingdao.aliyuncs.com", 9 | ], 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-video", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "test": "vitest", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/react": "^2.3.5", 14 | "@emotion/react": "^11.10.4", 15 | "@emotion/styled": "^11.10.4", 16 | "@next-auth/prisma-adapter": "^1.0.4", 17 | "@prisma/client": "4.4.0", 18 | "framer-motion": "^7.5.3", 19 | "next": "12.3.1", 20 | "next-auth": "^4.12.3", 21 | "nodemailer": "^2.7.2", 22 | "react": "18.2.0", 23 | "react-dom": "18.2.0", 24 | "swr": "^1.3.0" 25 | }, 26 | "devDependencies": { 27 | "@testing-library/jest-dom": "^5.16.5", 28 | "@testing-library/react": "^13.4.0", 29 | "@types/node": "^18.8.3", 30 | "@types/react": "18.0.21", 31 | "@vitejs/plugin-react": "^2.1.0", 32 | "@vitest/coverage-c8": "^0.24.3", 33 | "autoprefixer": "^10.4.12", 34 | "eslint": "8.24.0", 35 | "eslint-config-next": "12.3.1", 36 | "jsdom": "^20.0.1", 37 | "postcss": "^8.4.17", 38 | "prisma": "^4.4.0", 39 | "tailwindcss": "^3.1.8", 40 | "ts-node": "^10.9.1", 41 | "typescript": "4.8.4", 42 | "vitest": "^0.24.3" 43 | }, 44 | "prisma": { 45 | "seed": "ts-node prisma/seed.ts" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | import { ChakraProvider } from "@chakra-ui/react"; 3 | import { SessionProvider } from "next-auth/react"; 4 | 5 | export default function App({ 6 | Component, 7 | pageProps: { session, ...pageProps }, 8 | }) { 9 | return ( 10 | 11 | 12 |
13 | 14 |
15 |
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /pages/api/auth/[...nextauth].ts: -------------------------------------------------------------------------------- 1 | import NextAuth, { NextAuthOptions } from "next-auth"; 2 | import EmailProvider from "next-auth/providers/email"; 3 | import GithubProvider from "next-auth/providers/github"; 4 | import CredentialsProvider from "next-auth/providers/credentials"; 5 | import prisma from "@/lib/prisma"; 6 | import { sendVerificationRequest } from "@/lib/sendVerificationRequest"; 7 | import { PrismaAdapter } from "@next-auth/prisma-adapter"; 8 | 9 | export const authOptions: NextAuthOptions = { 10 | //debug: true, 11 | adapter: PrismaAdapter(prisma), 12 | providers: [ 13 | CredentialsProvider({ 14 | name: "密码登录", 15 | credentials: { 16 | email: { 17 | label: "邮箱", 18 | type: "text", 19 | placeholder: "请输入邮箱", 20 | }, 21 | password: { 22 | label: "密码", 23 | type: "password", 24 | placeholder: "请输入密码", 25 | }, 26 | }, 27 | async authorize(credentials, req) { 28 | console.log(credentials); 29 | // TODO 30 | // const maybeUser= await prisma.user.findFirst({where:{ 31 | // email: credentials.email, 32 | // }}) 33 | 34 | const user = { 35 | id: "cl95b4ny10000fjnuhm7rvys5", 36 | name: "xiaoma", 37 | image: "https://avatars.githubusercontent.com/u/9312044?v=4", 38 | email: "164377467@qq.com", 39 | }; 40 | 41 | if (user) { 42 | // 返回的对象将保存才JWT 的用户属性中 43 | return user; 44 | } else { 45 | // 如果返回null,则会显示一个错误,建议用户检查其详细信息。 46 | return null; 47 | // 跳转到错误页面,并且携带错误信息 http://localhost:3000/api/auth/error?error=用户名或密码错误 48 | //throw new Error("用户名或密码错误"); 49 | } 50 | }, 51 | }), 52 | EmailProvider({ 53 | server: process.env.EMAIL_SERVER, 54 | from: process.env.EMAIL_FROM, 55 | sendVerificationRequest, 56 | }), 57 | // OAuth authentication providers... 58 | GithubProvider({ 59 | clientId: process.env.GITHUB_ID, 60 | clientSecret: process.env.GITHUB_SECRET, 61 | httpOptions: { 62 | timeout: 50000, 63 | }, 64 | }), 65 | ], 66 | pages: { 67 | signIn: "/auth/login", 68 | }, 69 | session: { 70 | strategy: "jwt", 71 | }, 72 | jwt: { 73 | secret: "test", 74 | }, 75 | callbacks: { 76 | async jwt({ token, user, account, profile, isNewUser }) { 77 | if (user) { 78 | token.id = user.id; 79 | } 80 | 81 | return token; 82 | }, 83 | session: async ({ session, token, user }) => { 84 | if (session?.user && token) { 85 | session.user.id = token.id as string; 86 | } 87 | return session; 88 | }, 89 | }, 90 | }; 91 | 92 | export default NextAuth(authOptions); 93 | -------------------------------------------------------------------------------- /pages/api/chapter.ts: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | import prisma from "@/lib/prisma"; 3 | import { makeSerializable } from "@/lib/util"; 4 | import { NextApiRequest, NextApiResponse } from "next"; 5 | 6 | export default async function handler( 7 | req: NextApiRequest, 8 | res: NextApiResponse 9 | ) { 10 | const { videoId, cursor } = req.query; 11 | if (!videoId) { 12 | res.status(400).json({ message: "videoId is required" }); 13 | } 14 | 15 | const data = await prisma.chapter.findMany({ 16 | cursor: cursor && { 17 | id: +cursor, 18 | }, 19 | take: 11, 20 | where: { 21 | videoId: +videoId, 22 | }, 23 | }); 24 | res.status(200).json({ 25 | data: makeSerializable(data.slice(0, 10)), 26 | nextCursor: data[10]?.id, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /pages/auth/login.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getCsrfToken } from "next-auth/react"; 3 | import { 4 | Text, 5 | Button, 6 | Input, 7 | Tabs, 8 | TabList, 9 | TabPanels, 10 | Tab, 11 | TabPanel, 12 | } from "@chakra-ui/react"; 13 | 14 | export default function Login({ csrfToken }) { 15 | return ( 16 |
17 | 18 | 26 | Next Video App 27 | 28 | 29 | 密码登录 30 | 邮箱注册 31 | 32 | 33 | 34 | 35 |
40 | 46 |
47 | 54 |
55 |
56 | 63 |
64 | 72 |
73 |
74 |
75 | 81 | 82 | 94 |
95 |
96 |
97 | 98 |
103 | 109 |
110 | 119 |
120 | 128 |
129 |
130 |
131 |
132 |
133 | ); 134 | } 135 | 136 | export async function getServerSideProps(context) { 137 | const csrfToken = await getCsrfToken(context); 138 | return { 139 | props: { csrfToken }, 140 | }; 141 | } 142 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { GetServerSideProps } from "next"; 3 | import prisma from "../lib/prisma"; 4 | import { makeSerializable } from "../lib/util"; 5 | import LoginBtn from "@/components/login-btn"; 6 | import VideoList from "@/components/video-list"; 7 | import { Video, User } from "@prisma/client"; 8 | 9 | type Props = { 10 | data: (Video & { 11 | author: User; 12 | })[]; 13 | }; 14 | 15 | export default function Page({ data }: Props) { 16 | return ( 17 |
18 |
19 |

Next Video

20 | 21 |
22 |
23 | 24 |
25 |
26 | ); 27 | } 28 | 29 | export const getServerSideProps: GetServerSideProps = async () => { 30 | const data = await prisma.video.findMany({ 31 | include: { author: true }, 32 | }); 33 | 34 | return { 35 | props: { data: makeSerializable(data) }, 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /pages/me.tsx: -------------------------------------------------------------------------------- 1 | import { authOptions } from "@/pages/api/auth/[...nextauth]"; 2 | import { unstable_getServerSession } from "next-auth/next"; 3 | import prisma from "@/lib/prisma"; 4 | import { Video, User } from "@prisma/client"; 5 | import { makeSerializable } from "@/lib/util"; 6 | import VideoList from "@/components/video-list"; 7 | 8 | type Props = { 9 | data: (Video & { 10 | author: User; 11 | })[]; 12 | }; 13 | 14 | export default function Page({ data }) { 15 | return ( 16 |
17 |
18 |

个人中心

19 |
20 |
21 | 22 |
23 |
24 | ); 25 | } 26 | 27 | export async function getServerSideProps(context) { 28 | const session = await unstable_getServerSession( 29 | context.req, 30 | context.res, 31 | authOptions 32 | ); 33 | 34 | if (!session) { 35 | return { 36 | redirect: { 37 | destination: "/", 38 | permanent: false, 39 | }, 40 | }; 41 | } 42 | 43 | const data = await prisma.video.findMany({ 44 | where: { 45 | authorId: session.user.id, 46 | }, 47 | include: { author: true }, 48 | }); 49 | 50 | return { 51 | props: { 52 | session, 53 | data: makeSerializable(data), 54 | }, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /pages/video/[id].tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from "react"; 2 | import { GetServerSideProps } from "next"; 3 | import Link from "next/link"; 4 | import Image from "next/image"; 5 | import prisma from "@/lib/prisma"; 6 | import { makeSerializable } from "@/lib/util"; 7 | import { Video, User, Chapter } from "@prisma/client"; 8 | import useSWRInfinite from "swr/infinite"; 9 | import useOnScreen from "@/hooks/useOnScreen"; 10 | 11 | type Props = { 12 | video: Video & { 13 | author: User; 14 | }; 15 | }; 16 | 17 | type Result = { data: Chapter[]; nextCursor: number }; 18 | 19 | const getKey = (pageIndex, previousPageData, videoId) => { 20 | // reached the end 21 | if (previousPageData && !previousPageData.data) return null; 22 | 23 | // 首页,没有 `previousPageData` 24 | if (pageIndex === 0) return `/api/chapter?videoId=${videoId}`; 25 | 26 | // 将游标添加到 API 27 | return `/api/chapter?cursor=${previousPageData.nextCursor}&videoId=${videoId}`; 28 | }; 29 | 30 | const fetcher = (url: string) => fetch(url).then((res) => res.json()); 31 | 32 | export default function Page({ video }: Props) { 33 | const ref: any = useRef(); 34 | const onScreen: boolean = useOnScreen(ref); 35 | 36 | const { data, error, size, setSize } = useSWRInfinite( 37 | (...args) => getKey(...args, video.id), 38 | fetcher, 39 | { 40 | revalidateFirstPage: false, 41 | } 42 | ); 43 | 44 | const hasNext = data && data[data.length - 1].nextCursor; 45 | const isLoadingInitialData = !data && !error; 46 | 47 | const isLoadingMore = 48 | isLoadingInitialData || 49 | (size > 0 && data && typeof data[size - 1] === "undefined"); 50 | 51 | useEffect(() => { 52 | if (onScreen && hasNext) { 53 | setSize(size + 1); 54 | } 55 | }, [onScreen, hasNext]); 56 | 57 | return ( 58 |
59 |

{video.title}

60 |
61 | {video.title} 62 |
63 |
{video.desc}
64 |

章节视频

65 |
66 |
67 | {data && 68 | data.map((pageData, index) => { 69 | // `data` 是每个页面 API 响应的数组。 70 | return pageData.data.map((item) => ( 71 | 90 | )); 91 | })} 92 |
93 |
94 | {isLoadingMore ? "Loading..." : hasNext ? "加载更多" : "没有数据了"} 95 |
96 |
97 |
98 | ); 99 | } 100 | 101 | export const getServerSideProps: GetServerSideProps = async (context) => { 102 | const video = await prisma.video.findUnique({ 103 | include: { 104 | author: true, 105 | }, 106 | where: { 107 | id: Number(context.params.id), 108 | }, 109 | }); 110 | 111 | return { 112 | props: { 113 | video: makeSerializable(video), 114 | }, 115 | }; 116 | }; 117 | -------------------------------------------------------------------------------- /pages/video/chapter/[id].tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from "react"; 2 | import { GetServerSideProps } from "next"; 3 | import prisma from "@/lib/prisma"; 4 | import { makeSerializable } from "@/lib/util"; 5 | import { Chapter } from "@prisma/client"; 6 | 7 | type Props = { 8 | chapter: Chapter; 9 | }; 10 | 11 | export default function Page({ chapter }: Props) { 12 | return ( 13 |
14 |
15 | 26 |
27 |
{chapter.title}
28 |
29 | ); 30 | } 31 | 32 | export const getServerSideProps: GetServerSideProps = async (context) => { 33 | const chapter = await prisma.chapter.findUnique({ 34 | where: { 35 | id: Number(context.params.id), 36 | }, 37 | }); 38 | 39 | return { 40 | props: { 41 | chapter: makeSerializable(chapter), 42 | }, 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prisma/example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": true, 3 | "data": { 4 | "type": "opencourse", 5 | "id": 1091, 6 | "title": "高中数学:三角", 7 | "en_title": "Trigonometry", 8 | "subtitle": "", 9 | "slug": "trigonometry", 10 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201602/18/1455776864827_2156.jpg", 11 | "brief": "课程名看上去很牛是不是?被骗了吧。看看前缀,tri-,你也许会认为三角(“trig”有时候会这么称呼它)和三角形有关。也许某种程度上是对的!三角的确是在研究三角形的属性。为什么这很重要?它会被用来测量精确的距离,特别是在工业方面,比如卫星系统,还有天文科学等。它不仅仅是一种空间概念。三角还在建筑和音乐中频繁出现。你也许会好奇 - 怎么三角会和音乐扯上关系?这真是个好问题,自己去课程里找答案吧!", 12 | "detail": "PHA+6K++56iL5ZCN55yL5LiK5Y675b6I54mb5piv5LiN5piv77yf6KKr6aqX5LqG5ZCn44CC55yL55yL5YmN57yA77yMdHJpLe+8jOS9oOS5n+iuuOS8muiupOS4uuS4ieinku+8iCZsZHF1bzt0cmlnJnJkcXVvO+acieaXtuWAmeS8mui/meS5iOensOWRvOWug++8ieWSjOS4ieinkuW9ouacieWFs+OAguS5n+iuuOafkOenjeeoi+W6puS4iuaYr+WvueeahO+8geS4ieinkueahOehruaYr+WcqOeglOeptuS4ieinkuW9oueahOWxnuaAp+OAguS4uuS7gOS5iOi/meW+iOmHjeimge+8n+Wug+S8muiiq+eUqOadpea1i+mHj+eyvuehrueahOi3neemu++8jOeJueWIq+aYr+WcqOW3peS4muaWuemdou+8jOavlOWmguWNq+aYn+ezu+e7n++8jOi/mOacieWkqeaWh+enkeWtpuetieOAguWug+S4jeS7heS7heaYr+S4gOenjeepuumXtOamguW/teOAguS4ieinkui/mOWcqOW7uuetkeWSjOmfs+S5kOS4remikee5geWHuueOsOOAguS9oOS5n+iuuOS8muWlveWlhyAtIOaAjuS5iOS4ieinkuS8muWSjOmfs+S5kOaJr+S4iuWFs+ezu++8n+i/meecn+aYr+S4quWlvemXrumimO+8jOiHquW3seWOu+ivvueoi+mHjOaJvuetlOahiOWQp++8gTwvcD48c2VjdGlvbj48aDU+56ysMeeroCZuYnNwO+ebuOS8vOS4ieinkuW9ouWSjOS4ieinkuavlDwvaDU+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtMSZuYnNwO+S4ieinkuavlCZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDk6MTYpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPihRQVEpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+MS0yJm5ic3A75aaC5L2V6K6h566X55u06KeS5LiJ6KeS5b2i55qE5LiJ6KeS5Ye95pWwJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxMjoxMCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOiQveWMl+mbtik8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4xLTMmbmJzcDvlpoLkvZXliKnnlKjkuInop5Lms5XmsYLnm7Top5LkuInop5LlvaLkuK3mnKrnn6XovrnnmoTplb/luqbvvIjkvovpopjvvIkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA3OjAzKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5rGf5rKi5aau5Y+vKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtNCZuYnNwO+aAjuagt+WcqOacieS4gOS4quacquefpeinkueahOaDheWGteS4i+ino+S4gOS4quebtOinkuS4ieinkuW9oijnpLrkvospJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNDozNCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KFNoZXJsb2NrKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtNSZuYnNwO+ebuOS8vOS4ieinkuW9ouWSjOS4ieinkuavlCZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDg6NTUpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijCpzg4LjlMQU1QKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtNiZuYnNwO0NoYWxsZW5nZSBwcm9ibGVtOiBWZXJpZnkgaWRlbnRpdGllcyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDU6MDYpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+MS03Jm5ic3A7Q2hhbGxlbmdlIHByb2JsZW06IE1hdGNoIHRyaWdvbm9tZXRyaWMgdmFsdWVzIGFuZCBzaWRlIHJhdGlvcyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDk6MjMpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+MS04Jm5ic3A7VGhlIHNpbmUgYW5kIGNvc2luZSBvZiBjb21wbGVtZW50YXJ5IGV4YW1wbGUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA0OjE0KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtOSZuYnNwO+S6kuS9meinkueahOato+S9meW8puWFs+ezu+W6lOeUqCZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDQ6MDQpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPihtQXRoKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjEtMTAmbmJzcDtVc2luZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc2luZSBhbmQgY29zaW5lIG9mIGNvbXBsZW1lbnRhcnkgYW5nbGVzIHRvIHNvbHZlIGEgcmVhbC13b3JsZCBwcm9ibGVtJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNTo0Myk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4xLTExJm5ic3A7SW50cm9kdWN0aW9uIHRvIHRoZSBQeXRoYWdvcmVhbiB0cmlnb25vbWV0cmljIGlkZW50aXR5Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNDoxNSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4xLTEyJm5ic3A7SG93IHRvIGZpbmQgdGhlIHJlY2lwcm9jYWwgdHJpZ29ub21ldHJpYyByYXRpb3Mgb2YgYW4gYW5nbGUgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNDo0Myk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4xLTEzJm5ic3A7SG93IHRvIHVzZSBjb3RhbmdlbnQgaW4gb3JkZXIgdG8gc29sdmUgZm9yIGEgc2lkZSBpbiBhIHJpZ2h0IHRyaWFuZ2xlIChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDY6MTEpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48aDU+56ysMueroCZuYnNwO+S4gOiIrOS4ieinkuW9ojwvaDU+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjItMSZuYnNwO0hvdyB0byBmaW5kIGEgbWlzc2luZyBzaWRlIGxlbmd0aCB1c2luZyB0aGUgbGF3IG9mIHNpbmVzIChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDU6NTcpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+Mi0yJm5ic3A7SG93IHRvIGZpbmQgYSBtaXNzaW5nIGFuZ2xlIG1lYXN1cmUgdXNpbmcgdGhlIGxhdyBvZiBzaW5lcyAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA1OjMzKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjItMyZuYnNwO1Byb29mIG9mIHRoZSBsYXcgb2Ygc2luZXMmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA2OjMzKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjItNCZuYnNwO0hvdyB0byBmaW5kIGEgbWlzc2luZyBzaWRlIGxlbmd0aCB1c2luZyB0aGUgbGF3IG9mIGNvc2luZXMgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNDozNyk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4yLTUmbmJzcDtIb3cgdG8gZmluZCBhIG1pc3NpbmcgYW5nbGUgbWVhc3VyZSB1c2luZyB0aGUgbGF3IG9mIGNvc2luZXMgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNjo0MCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4yLTYmbmJzcDtQcm9vZiBvZiB0aGUgbGF3IG9mIGNvc2luZXMmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA5OjI2KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjItNyZuYnNwO1dvcmQgcHJvYmxlbTogU29sdmluZyBmb3IgYSBzaWRlIGxlbmd0aCBpbiBhIGdlbmVyYWwgdHJpYW5nbGUgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNTo1OCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxoNT7nrKwz56ugJm5ic3A75q2j5bym77yM5L2Z5bym77yM5q2j5YiH55qE5Y2V5L2N5ZyG5a6a5LmJPC9oNT48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My0xJm5ic3A7SW50cm9kdWN0aW9uIHRvIHJhZGlhbnMmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjEwOjQ5KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtMiZuYnNwO0NvbnZlcnRpbmcgYmV0d2VlbiByYWRpYW5zIGFuZCBkZWdyZWVzJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNzoxMCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTMmbmJzcDtIb3cgdG8gY29udmVydCBmcm9tIGRlZ3JlZXMgdG8gcmFkaWFucyAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA3OjAwKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtNCZuYnNwO0hvdyB0byBjb252ZXJ0IGZyb20gcmFkaWFucyB0byBkZWdyZWVzIChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDM6MTkpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My01Jm5ic3A7Um90YXRpb24gYnkgcmFkaWFucyBhbmQgcXVhZHJhbnRzJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowMzo1MCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTYmbmJzcDtUaGUgdW5pdCBjaXJjbGUgZGVmaW5pdGlvbiBvZiBzaW5lLCBjb3NpbmUsIGFuZCB0YW5nZW50Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowOTowMyk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTcmbmJzcDtIb3cgdG8gZmluZCB0cmlnb25vbWV0cmljIHZhbHVlcyB1c2luZyB0aGUgdW5pdCBjaXJjbGUgZGVmaW5pdGlvbiAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA0OjExKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtOCZuYnNwO01hdGNoaW5nIHRoZSBTT0ggQ0FIIFRPQSBhbmQgdGhlIHVuaXQgY2lyY2xlIGRlZmluaXRpb25zIG9mIHRoZSB0cmlnb25vbWV0cmljIGZ1bmN0aW9ucyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDY6NTYpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My05Jm5ic3A7VGhlIGdyYXBoIG9mIHRoZSBzaW5lIGZ1bmN0aW9uJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowOToyMSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTEwJm5ic3A7VGhlIGdyYXBoIG9mIHRoZSB0YW5nZW50IGZ1bmN0aW9uJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxMDoxMSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTExJm5ic3A7RmluZGluZyB0aGUgaW50ZXJzZWN0aW9uIHBvaW50cyBvZiB0aGUgZ3JhcGhzIG9mIHNpbmUgYW5kIGNvc2luZSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTE6MDYpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My0xMiZuYnNwO1NpbmUgYW5kIGNvc2luZSBpZGVudGl0aWVzIHRoYXQgYXJpc2UgZnJvbSB0aGUgc3ltbWV0cnkgb2YgdGhlIHVuaXQgY2lyY2xlJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNzo1Nyk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTEzJm5ic3A7VGFuZ2VudCBpZGVudGl0aWVzIHRoYXQgYXJpc2UgZnJvbSB0aGUgc3ltbWV0cnkgb2YgdGhlIHVuaXQgY2lyY2xlJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNzoxNCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij4zLTE0Jm5ic3A7U2luZSBhbmQgY29zaW5lIGlkZW50aXRpZXMgdGhhdCBhcmlzZSBmcm9tIGFuZ2xlIHJvdGF0aW9ucyBvbiB0aGUgdW5pdCBjaXJjbGUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA2OjEyKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtMTUmbmJzcDtJZGVudGl0eSB0aGF0IGFyaXNlcyBmcm9tIHRoZSBwZXJpb2RpY2l0eSBvZiB0aGUgdGFuZ2VudCBmdW5jdGlvbiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDQ6MDEpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My0xNiZuYnNwO0ZpbmRpbmcgdGhlIHRyaWdvbm9tZXRyaWMgdmFsdWVzIG9mIGEgc3BlY2lhbCBhbmdsZSBpbiB0aGUgdW5pdCBjaXJjbGUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA3OjUyKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtMTcmbmJzcDtQcm9vZiBvZiB0aGUgUHl0aGFnb3JlYW4gdHJpZ29ub21ldHJpYyBpZGVudGl0eSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDY6MTEpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My0xOCZuYnNwO0hvdyB0byBmaW5kIGEgdHJpZyB2YWx1ZSBvZiBhbiBhbmdsZSBnaXZlbiBhbm90aGVyIHRyaWcgdmFsdWUgb2YgdGhlIGFuZ2xlIChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDY6MTQpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+My0xOSZuYnNwO1RhdSB2ZXJzdXMgcGkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjE2OjIxKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjMtMjAmbmJzcDtQaSBpcyAoc3RpbGwpIHdyb25nJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNToxNik8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxoNT7nrKw056ugJm5ic3A75LiJ6KeS5Ye95pWw5puy57q/PC9oNT48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NC0xJm5ic3A7VGhlIGdyYXBoIG9mIHRoZSBzaW5lIGZ1bmN0aW9uJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowOToyMSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij40LTImbmJzcDtUaGUgZ3JhcGggb2YgdGhlIHRhbmdlbnQgZnVuY3Rpb24mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjEwOjExKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtMyZuYnNwO0ZpbmRpbmcgdGhlIGludGVyc2VjdGlvbiBwb2ludHMgb2YgdGhlIGdyYXBocyBvZiBzaW5lIGFuZCBjb3NpbmUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjExOjA2KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtNCZuYnNwO01pZGxpbmUsIGFtcGxpdHVkZSBhbmQgcGVyaW9kIG9mIGEgc2ludXNvaWRhbCBmdW5jdGlvbiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDQ6NTcpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NC01Jm5ic3A7SG93IHRvIGZpbmQgdGhlIGFtcGxpdHVkZSBhbmQgcGVyaW9kIG9mIGEgc2ludXNvaWRhbCBmdW5jdGlvbnMgZnJvbSBpdHMgZm9ybXVsYSAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA4OjIwKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtNiZuYnNwO0hvdyB0byBmaW5kIHRoZSBhbXBsaXR1ZGUgYW5kIHBlcmlvZCBvZiBhIHNpbnVzb2lkYWwgZnVuY3Rpb25zIGZyb20gaXRzIGZvcm11bGEgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowODoyMCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij40LTcmbmJzcDtHcmFwaCBvZiBhIHNpbnVzb2lkYWwgZnVuY3Rpb24gd2l0aCB2ZXJ0aWNhbCBzdHJldGNoIGFuZCBob3Jpem9udGFsIHJlZmxlY3Rpb24mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjEyOjUxKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtOCZuYnNwO0dyYXBoIG9mIGEgc2ludXNvaWRhbCBmdW5jdGlvbiB3aXRoIHZlcnRpY2FsIGFuZCBob3Jpem9udGFsIHN0cmV0Y2hlcyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTA6NDIpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NC05Jm5ic3A7SG93IHRvIGNvbnN0cnVjdCBhIHNpbnVzb2lkYWwgZnVuY3Rpb24gZnJvbSBpdHMgZ3JhcGgmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA1OjIxKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtMTAmbmJzcDtXb3JkIHByb2JsZW06IENvbnN0cnVjdGluZyBhIHNpbnVzb2lkYWwgbW9kZWwgdXNpbmcgc2luZSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTA6NTQpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NC0xMSZuYnNwO1dvcmQgcHJvYmxlbTogQ29uc3RydWN0aW5nIGEgc2ludXNvaWRhbCBtb2RlbCB1c2luZyBjb3NpbmUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA3OjA4KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjQtMTImbmJzcDtXb3JkIHByb2JsZW06IENvbnN0cnVjdGluZyBhIHNpbnVzb2lkYWwgbW9kZWwgd2l0aCBwaGFzZSBzaGlmdCZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDc6NDIpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48aDU+56ysNeeroCZuYnNwO+S4ieinkuaWueeoi+etieW8jzwvaDU+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtMSZuYnNwO0ludHJvZHVjdGlvbiB0byBhcmNzaW5lJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxMDozNSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTImbmJzcDtJbnRyb2R1Y3Rpb24gdG8gYXJjdGFuZ2VudCZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTA6MDUpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0zJm5ic3A7SW50cm9kdWN0aW9uIHRvIGFyY2Nvc2luZSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTM6MzcpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS00Jm5ic3A7SG93IHRvIHJlc3RyaWN0IHRoZSBkb21haW4gb2YgYSBmdW5jdGlvbiB0byBtYWtlIGl0IGludmVydGlibGUgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNTo1OSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTUmbmJzcDtSZXN0cmljdGluZyB0aGUgZG9tYWluIG9mIGEgdGFuZ2VudCBmdW5jdGlvbiB0byBtYWtlIGl0IGludmVydGlibGUmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA5OjQzKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtNiZuYnNwO0hvdyB0byBmaW5kIGFuIGFuZ2xlIGdpdmVuIGl0cyB0YW5nZW50IHVzaW5nIGEgY2FsY3VsYXRvciAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjAzOjM0KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtNyZuYnNwO0hvdyB0byBmaW5kIGFsbCBzb2x1dGlvbnMgdG8gYW4gZXF1YXRpb24gb2YgdGhlIGZvcm0gc2luKHgpPWQgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNjowOSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTgmbmJzcDtTb2x2aW5nIGNvcyjOuCk9MSBhbmQgY29zKM64KT0tMSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDU6MTcpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS05Jm5ic3A7V29yZCBwcm9ibGVtOiBTb2x2aW5nIGEgc2ludXNvaWRhbCBtb2RlbGluZyBmdW5jdGlvbiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDg6MjIpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0xMCZuYnNwO1JldmlldyBvZiB0aGUgdHJpZ29ub21ldHJpYyBhbmdsZSBhZGRpdGlvbiBpZGVudGl0aWVzJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxMTowNik8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTExJm5ic3A7SG93IHRvIHVzZSB0aGUgY29zaW5lIGFuZ2xlIGFkZGl0aW9uIGlkZW50aXR5IChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDU6MTMpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0xMiZuYnNwO0hvdyB0byB1c2UgdGhlIGNvc2luZSBkb3VibGUtYW5nbGUgaWRlbnRpdHkgKGV4YW1wbGUpJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowMzoyNyk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTEzJm5ic3A7UHJvb2Ygb2YgdGhlIHNpbmUgYW5nbGUgYWRkaXRpb24gaWRlbnRpdHkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA4OjI1KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtMTQmbmJzcDtQcm9vZiBvZiB0aGUgY29zaW5lIGFuZ2xlIGFkZGl0aW9uIGlkZW50aXR5Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowNjowMSk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTE1Jm5ic3A7SG93IHRvIGZpbmQgdHJpZ29ub21ldHJpYyB2YWx1ZXMgdXNpbmcgdGhlIGFuZ2xlIGFkZGl0aW9uIGZvcm11bGFzIChleGFtcGxlKSZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDg6MzQpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0xNiZuYnNwO0hvdyB0byBmaW5kIGEgc2lkZSBsZW5ndGggdXNpbmcgdGhlIHRyaWcgYW5nbGUgYWRkaXRpb24gaWRlbnRpdGllcyAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA1OjE5KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtMTcmbmJzcDtIb3cgdG8gbWFuaXB1bGF0ZSBleHByZXNzaW9ucyB1c2luZyB0aGUgdHJpZyBhbmdsZSBhZGRpdGlvbiBmb3JtdWxhcyAoZXhhbXBsZSkmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjA2OjExKTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtMTgmbmJzcDtSZXdyaXRpbmcgdHJpZ29ub21ldHJpYyBleHByZXNzaW9ucyB1c2luZyB0cmlnb25vbWV0cmljIGlkZW50aXRpZXMmbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuNmVtOyI+KDAwOjAzOjA2KTwvc3Bhbj4mbmJzcDs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjAuN2VtO2NvbG9yOiM5ZDlkOWQ7Ij4o5b6F6K+RKTwvc3Bhbj48L3A+PHAgc3R5bGU9InRleHQtaW5kZW50OjFlbTsiPjUtMTkmbmJzcDtDaGFsbGVuZ2UgcHJvYmxlbTogRmluZCB0aGUgYXJlYSBvZiBhIHRyaWFuZ2xlJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxNjozNCk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTIwJm5ic3A7Q2hhbGxlbmdlIHByb2JsZW06IEZpbmQgdGhlIGFyZWEgb2YgYSBoZXhhZ29uJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDoxODoxNyk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTIxJm5ic3A7Q2hhbGxlbmdlIHByb2JsZW06IEZpbmQgY29zaW5lIG9mIHN1bSBvZiBhbmdsZXMgZ2l2ZW4gdGhlaXIgY29zaW5lcyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MDc6MTMpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0yMiZuYnNwO0NoYWxsZW5nZSBwcm9ibGVtOiBSZXdyaXRlIGEgdHJpZ29ub21ldHJpYyBlcXVhdGlvbiBvZiBhbmdsZXMgaW4gYXJpdGhtZXRpYyBwcm9ncmVzc2lvbiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTU6MTIpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0yMyZuYnNwO0NoYWxsZW5nZSBwcm9ibGVtOiBGaW5kIHRoZSBtYXhpbXVtIHZhbHVlIG9mIGEgdHJpZ29ub21ldHJpYyBleHByZXNzaW9uJm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjZlbTsiPigwMDowOTo1Myk8L3NwYW4+Jm5ic3A7PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTowLjdlbTtjb2xvcjojOWQ5ZDlkOyI+KOW+heivkSk8L3NwYW4+PC9wPjxwIHN0eWxlPSJ0ZXh0LWluZGVudDoxZW07Ij41LTI0Jm5ic3A7Q2hhbGxlbmdlIHByb2JsZW06IFN5c3RlbSBvZiB0cmlnb25vbWV0cmljIGVxdWF0aW9ucyZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTI6NDgpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48cCBzdHlsZT0idGV4dC1pbmRlbnQ6MWVtOyI+NS0yNSZuYnNwO0NoYWxsZW5nZSBwcm9ibGVtOiBDb25kaXRpb25zIG9uIGFuZ2xlIGZvciBhIHN5c3RlbSBvZiBlcXVhdGlvbiBoYXZpbmcgYSBzb2x1dGlvbiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC42ZW07Ij4oMDA6MTE6MDIpPC9zcGFuPiZuYnNwOzxzcGFuIHN0eWxlPSJmb250LXNpemU6MC43ZW07Y29sb3I6IzlkOWQ5ZDsiPijlvoXor5EpPC9zcGFuPjwvcD48L3NlY3Rpb24+", 13 | "instructor_id": [716], 14 | "duration_hour": 10.15, 15 | "duration_secs": 0, 16 | "onupdate": true, 17 | "join_course_total": 215, 18 | "promo_id": null, 19 | "institute": { 20 | "id": 216, 21 | "name": "可汗学院", 22 | "en_name": "Khan Academy", 23 | "logo_url": "http://gcdp.oss-cn-qingdao.aliyuncs.com/201603/1/1456788038181_4161.jpg", 24 | "brief": "从幼儿园教到大学的美国免费在线课堂", 25 | "detail": "", 26 | "instructors": [ 27 | { 28 | "id": 716, 29 | "name": "Salman Khan", 30 | "avatar_url": "http://gcdp.oss-cn-qingdao.aliyuncs.com/201603/1/1456788242021_5284.jpg", 31 | "brief": "", 32 | "institute_id": 216 33 | } 34 | ] 35 | }, 36 | "chapter_total": 5, 37 | "lecture_total": 77, 38 | "video_total": 77, 39 | "article_total": 0, 40 | "quiz_total": 0, 41 | "attachment_total": 0, 42 | "translated_chapter_total": 0, 43 | "translated_lecture_total": 6, 44 | "translated_video_total": 6, 45 | "translated_article_total": 0, 46 | "translated_quiz_total": 0, 47 | "translated_attachment_total": 0, 48 | "category_id": 15, 49 | "join_course_state": "unjoin", 50 | "promo": null, 51 | "outlines": [ 52 | { 53 | "id": 2158, 54 | "index": 1, 55 | "title": "相似三角形和三角比", 56 | "en_title": "Trigonometry with right triangles", 57 | "lectures": [ 58 | { 59 | "id": 33029, 60 | "index": 1, 61 | "title": "三角比", 62 | "en_title": "Introduction to the trigonometric ratios", 63 | "brief": "", 64 | "onshow": true, 65 | "attachment_total": 0, 66 | "attachments": [], 67 | "type": "video", 68 | "resource": { 69 | "id": 19674, 70 | "duration_secs": 556, 71 | "duration_msecs": 556333, 72 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457032915450_8750.jpg", 73 | "lecture_id": 33029, 74 | "guid": "2a29014d-e33b-4073-b21d-018bb3d6b435", 75 | "brief": "还在为三角函数的定义、记忆和解答而发懵?Sal用简洁的讲解迅速破解了还提供了助记方法,一起来看看吧", 76 | "content": [ 77 | { 78 | "name": "360p", 79 | "url": "http://alicdn.yxgapp.com/miniVideos/2a29014d-e33b-4073-b21d-018bb3d6b435.mp4", 80 | "size": 8255203, 81 | "size_mb": 7.87, 82 | "priority": 1 83 | } 84 | ], 85 | "comment_number": 4, 86 | "collect_number": 8, 87 | "source_url": "http://www.youtube.com/watch?v=Jsiy4TxgIME", 88 | "translate_status": "complete" 89 | } 90 | }, 91 | { 92 | "id": 33030, 93 | "index": 2, 94 | "title": "如何计算直角三角形的三角函数", 95 | "en_title": "How to find trigonometric ratios in a right triangle (examples)", 96 | "brief": "", 97 | "onshow": true, 98 | "attachment_total": 0, 99 | "attachments": [], 100 | "type": "video", 101 | "resource": { 102 | "id": 19675, 103 | "duration_secs": 730, 104 | "duration_msecs": 730933, 105 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457032965201_8611.jpg", 106 | "lecture_id": 33030, 107 | "guid": "11eda71d-b269-42e6-ac15-7ce44cbfb8cb", 108 | "brief": "本课程通过举例说明,介绍了直角三角形的正弦,余弦,正切该如何计算", 109 | "content": [ 110 | { 111 | "name": "360p", 112 | "url": "http://alicdn.yxgapp.com/miniVideos/11eda71d-b269-42e6-ac15-7ce44cbfb8cb.mp4", 113 | "size": 10631532, 114 | "size_mb": 10.14, 115 | "priority": 1 116 | } 117 | ], 118 | "comment_number": 2, 119 | "collect_number": 2, 120 | "source_url": "http://www.youtube.com/watch?v=G-T_6hCdMQc", 121 | "translate_status": "complete" 122 | } 123 | }, 124 | { 125 | "id": 33031, 126 | "index": 3, 127 | "title": "如何利用三角法求直角三角形中未知边的长度(例题)", 128 | "en_title": "How to find missing sides in a right triangle using trigonometry (example)", 129 | "brief": "", 130 | "onshow": true, 131 | "attachment_total": 0, 132 | "attachments": [], 133 | "type": "video", 134 | "resource": { 135 | "id": 19676, 136 | "duration_secs": 423, 137 | "duration_msecs": 423533, 138 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457032987406_6451.jpg", 139 | "lecture_id": 33031, 140 | "guid": "bf5d6b8f-adbc-43f3-9b69-ba5ddfda79f8", 141 | "brief": "利用三角函数解直角三角形的简单应用(例题)", 142 | "content": [ 143 | { 144 | "name": "360p", 145 | "url": "http://alicdn.yxgapp.com/miniVideos/bf5d6b8f-adbc-43f3-9b69-ba5ddfda79f8.mp4", 146 | "size": 6462938, 147 | "size_mb": 6.16, 148 | "priority": 1 149 | } 150 | ], 151 | "comment_number": 0, 152 | "collect_number": 0, 153 | "source_url": "http://www.youtube.com/watch?v=l5VbdqRjTXc", 154 | "translate_status": "complete" 155 | } 156 | }, 157 | { 158 | "id": 33032, 159 | "index": 4, 160 | "title": "怎样在有一个未知角的情况下解一个直角三角形(示例)", 161 | "en_title": "How to solve a right triangle word problem with a missing angle (example)", 162 | "brief": "", 163 | "onshow": true, 164 | "attachment_total": 0, 165 | "attachments": [], 166 | "type": "video", 167 | "resource": { 168 | "id": 19677, 169 | "duration_secs": 274, 170 | "duration_msecs": 274533, 171 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033004958_609.jpg", 172 | "lecture_id": 33032, 173 | "guid": "bb64357e-cadf-4f07-9dc5-045144ae4943", 174 | "brief": "本节介绍了三角形的正弦余弦和正切,主要讲解了正切与反正切。", 175 | "content": [ 176 | { 177 | "name": "360p", 178 | "url": "http://alicdn.yxgapp.com/miniVideos/bb64357e-cadf-4f07-9dc5-045144ae4943.mp4", 179 | "size": 4242851, 180 | "size_mb": 4.05, 181 | "priority": 1 182 | } 183 | ], 184 | "comment_number": 0, 185 | "collect_number": 0, 186 | "source_url": "http://www.youtube.com/watch?v=aHzd-u35LuA", 187 | "translate_status": "complete" 188 | } 189 | }, 190 | { 191 | "id": 33033, 192 | "index": 5, 193 | "title": "相似三角形和三角比", 194 | "en_title": "Triangle similarity and the trigonometric ratios", 195 | "brief": "", 196 | "onshow": true, 197 | "attachment_total": 0, 198 | "attachments": [], 199 | "type": "video", 200 | "resource": { 201 | "id": 19678, 202 | "duration_secs": 535, 203 | "duration_msecs": 535733, 204 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033027742_9570.jpg", 205 | "lecture_id": 33033, 206 | "guid": "37ad2868-d32a-4d7f-bb6f-18513f056d4b", 207 | "brief": "视频从直角三角形的相似入手,介绍了正弦,余弦以及正切", 208 | "content": [ 209 | { 210 | "name": "360p", 211 | "url": "http://alicdn.yxgapp.com/miniVideos/37ad2868-d32a-4d7f-bb6f-18513f056d4b.mp4", 212 | "size": 7876216, 213 | "size_mb": 7.51, 214 | "priority": 1 215 | } 216 | ], 217 | "comment_number": 0, 218 | "collect_number": 0, 219 | "source_url": "http://www.youtube.com/watch?v=QuZMXVJNLCo", 220 | "translate_status": "complete" 221 | } 222 | }, 223 | { 224 | "id": 33034, 225 | "index": 6, 226 | "title": null, 227 | "en_title": "Challenge problem: Verify identities", 228 | "brief": "", 229 | "onshow": true, 230 | "attachment_total": 0, 231 | "attachments": [], 232 | "type": "video", 233 | "resource": { 234 | "id": 19679, 235 | "duration_secs": 306, 236 | "duration_msecs": 535733, 237 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033049856_9314.jpg", 238 | "lecture_id": 33034, 239 | "guid": "eac41dac-ac22-42d0-8371-2abe60ec67c2", 240 | "brief": "", 241 | "content": [ 242 | { 243 | "name": "360p", 244 | "url": "http://alicdn.yxgapp.com/miniVideos/eac41dac-ac22-42d0-8371-2abe60ec67c2.mp4", 245 | "size": 7876216, 246 | "size_mb": 7.51, 247 | "priority": 1 248 | } 249 | ], 250 | "comment_number": 0, 251 | "collect_number": 0, 252 | "source_url": "http://www.youtube.com/watch?v=TugWqiUjOU4", 253 | "translate_status": "translation-not-started" 254 | } 255 | }, 256 | { 257 | "id": 33035, 258 | "index": 7, 259 | "title": null, 260 | "en_title": "Challenge problem: Match trigonometric values and side ratios", 261 | "brief": "", 262 | "onshow": true, 263 | "attachment_total": 0, 264 | "attachments": [], 265 | "type": "video", 266 | "resource": { 267 | "id": 19680, 268 | "duration_secs": 563, 269 | "duration_msecs": 563467, 270 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033074185_8991.jpg", 271 | "lecture_id": 33035, 272 | "guid": "8637ac7b-9e63-4f0b-a7be-49fb09c102ac", 273 | "brief": "", 274 | "content": [ 275 | { 276 | "name": "360p", 277 | "url": "http://alicdn.yxgapp.com/miniVideos/8637ac7b-9e63-4f0b-a7be-49fb09c102ac.mp4", 278 | "size": 9010651, 279 | "size_mb": 8.59, 280 | "priority": 1 281 | } 282 | ], 283 | "comment_number": 0, 284 | "collect_number": 1, 285 | "source_url": "http://www.youtube.com/watch?v=EBKNtjZAjXg", 286 | "translate_status": "translation-not-started" 287 | } 288 | }, 289 | { 290 | "id": 33036, 291 | "index": 8, 292 | "title": null, 293 | "en_title": "The sine and cosine of complementary example", 294 | "brief": "", 295 | "onshow": true, 296 | "attachment_total": 0, 297 | "attachments": [], 298 | "type": "video", 299 | "resource": { 300 | "id": 19681, 301 | "duration_secs": 254, 302 | "duration_msecs": 254333, 303 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033092022_6360.jpg", 304 | "lecture_id": 33036, 305 | "guid": "48fc34d4-d450-42f9-837c-58a9e963f93a", 306 | "brief": "", 307 | "content": [ 308 | { 309 | "name": "360p", 310 | "url": "http://alicdn.yxgapp.com/miniVideos/48fc34d4-d450-42f9-837c-58a9e963f93a.mp4", 311 | "size": 3659532, 312 | "size_mb": 3.49, 313 | "priority": 1 314 | } 315 | ], 316 | "comment_number": 0, 317 | "collect_number": 0, 318 | "source_url": "http://www.youtube.com/watch?v=BLQNL_UGONg", 319 | "translate_status": "translation-not-started" 320 | } 321 | }, 322 | { 323 | "id": 33037, 324 | "index": 9, 325 | "title": "互余角的正余弦关系应用", 326 | "en_title": "Using the relationship between sine and cosine of complementary angles to solve a geometry problem", 327 | "brief": "", 328 | "onshow": true, 329 | "attachment_total": 0, 330 | "attachments": [], 331 | "type": "video", 332 | "resource": { 333 | "id": 19682, 334 | "duration_secs": 244, 335 | "duration_msecs": 244667, 336 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033106742_3639.jpg", 337 | "lecture_id": 33037, 338 | "guid": "6a949a83-275e-4efc-b56f-b730fb30ae43", 339 | "brief": "教你一个数学小知识 没有弄懂的同学来看看吧~", 340 | "content": [ 341 | { 342 | "name": "360p", 343 | "url": "http://alicdn.yxgapp.com/miniVideos/6a949a83-275e-4efc-b56f-b730fb30ae43.mp4", 344 | "size": 3597480, 345 | "size_mb": 3.43, 346 | "priority": 1 347 | } 348 | ], 349 | "comment_number": 0, 350 | "collect_number": 0, 351 | "source_url": "http://www.youtube.com/watch?v=yiH6GoscimY", 352 | "translate_status": "complete" 353 | } 354 | }, 355 | { 356 | "id": 33038, 357 | "index": 10, 358 | "title": null, 359 | "en_title": "Using the relationship between sine and cosine of complementary angles to solve a real-world problem", 360 | "brief": "", 361 | "onshow": true, 362 | "attachment_total": 0, 363 | "attachments": [], 364 | "type": "video", 365 | "resource": { 366 | "id": 19683, 367 | "duration_secs": 343, 368 | "duration_msecs": 244667, 369 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033127853_8269.jpg", 370 | "lecture_id": 33038, 371 | "guid": "ee808261-bac7-4829-9126-f717cbd3668a", 372 | "brief": "", 373 | "content": [ 374 | { 375 | "name": "360p", 376 | "url": "http://alicdn.yxgapp.com/miniVideos/ee808261-bac7-4829-9126-f717cbd3668a.mp4", 377 | "size": 3597480, 378 | "size_mb": 3.43, 379 | "priority": 1 380 | } 381 | ], 382 | "comment_number": 0, 383 | "collect_number": 0, 384 | "source_url": "http://www.youtube.com/watch?v=Z5EnuVJawmY", 385 | "translate_status": "translation-not-started" 386 | } 387 | }, 388 | { 389 | "id": 33039, 390 | "index": 11, 391 | "title": null, 392 | "en_title": "Introduction to the Pythagorean trigonometric identity", 393 | "brief": "", 394 | "onshow": true, 395 | "attachment_total": 0, 396 | "attachments": [], 397 | "type": "video", 398 | "resource": { 399 | "id": 19684, 400 | "duration_secs": 255, 401 | "duration_msecs": 255800, 402 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033138479_2559.jpg", 403 | "lecture_id": 33039, 404 | "guid": "62973fac-c4ff-4802-809d-07d182d4291d", 405 | "brief": "", 406 | "content": [ 407 | { 408 | "name": "360p", 409 | "url": "http://alicdn.yxgapp.com/miniVideos/62973fac-c4ff-4802-809d-07d182d4291d.mp4", 410 | "size": 3670799, 411 | "size_mb": 3.5, 412 | "priority": 1 413 | } 414 | ], 415 | "comment_number": 0, 416 | "collect_number": 0, 417 | "source_url": "http://www.youtube.com/watch?v=HnDvUaVjQ1I", 418 | "translate_status": "translation-not-started" 419 | } 420 | }, 421 | { 422 | "id": 33040, 423 | "index": 12, 424 | "title": null, 425 | "en_title": "How to find the reciprocal trigonometric ratios of an angle (example)", 426 | "brief": "", 427 | "onshow": true, 428 | "attachment_total": 0, 429 | "attachments": [], 430 | "type": "video", 431 | "resource": { 432 | "id": 19685, 433 | "duration_secs": 283, 434 | "duration_msecs": 283933, 435 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033152344_4588.jpg", 436 | "lecture_id": 33040, 437 | "guid": "283499f9-9120-4c64-8556-ae4c5b641b83", 438 | "brief": "", 439 | "content": [ 440 | { 441 | "name": "360p", 442 | "url": "http://alicdn.yxgapp.com/miniVideos/283499f9-9120-4c64-8556-ae4c5b641b83.mp4", 443 | "size": 4349026, 444 | "size_mb": 4.15, 445 | "priority": 1 446 | } 447 | ], 448 | "comment_number": 0, 449 | "collect_number": 0, 450 | "source_url": "http://www.youtube.com/watch?v=Q7htxHDN8LE", 451 | "translate_status": "translation-not-started" 452 | } 453 | }, 454 | { 455 | "id": 33041, 456 | "index": 13, 457 | "title": null, 458 | "en_title": "How to use cotangent in order to solve for a side in a right triangle (example)", 459 | "brief": "", 460 | "onshow": true, 461 | "attachment_total": 0, 462 | "attachments": [], 463 | "type": "video", 464 | "resource": { 465 | "id": 19686, 466 | "duration_secs": 371, 467 | "duration_msecs": 371333, 468 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033171164_3792.jpg", 469 | "lecture_id": 33041, 470 | "guid": "cae558d2-8427-40f5-9ecb-dae6cba5c788", 471 | "brief": "", 472 | "content": [ 473 | { 474 | "name": "360p", 475 | "url": "http://alicdn.yxgapp.com/miniVideos/cae558d2-8427-40f5-9ecb-dae6cba5c788.mp4", 476 | "size": 5675893, 477 | "size_mb": 5.41, 478 | "priority": 1 479 | } 480 | ], 481 | "comment_number": 0, 482 | "collect_number": 0, 483 | "source_url": "http://www.youtube.com/watch?v=rufFQZDDXCE", 484 | "translate_status": "translation-not-started" 485 | } 486 | } 487 | ] 488 | }, 489 | { 490 | "id": 2159, 491 | "index": 2, 492 | "title": "一般三角形", 493 | "en_title": "Trigonometry with general triangles", 494 | "lectures": [ 495 | { 496 | "id": 33042, 497 | "index": 1, 498 | "title": null, 499 | "en_title": "How to find a missing side length using the law of sines (example)", 500 | "brief": "", 501 | "onshow": true, 502 | "attachment_total": 0, 503 | "attachments": [], 504 | "type": "video", 505 | "resource": { 506 | "id": 19687, 507 | "duration_secs": 357, 508 | "duration_msecs": 357600, 509 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033189999_9093.jpg", 510 | "lecture_id": 33042, 511 | "guid": "cde064b2-3334-47a8-a363-9648845843d9", 512 | "brief": "", 513 | "content": [ 514 | { 515 | "name": "360p", 516 | "url": "http://alicdn.yxgapp.com/miniVideos/cde064b2-3334-47a8-a363-9648845843d9.mp4", 517 | "size": 5411507, 518 | "size_mb": 5.16, 519 | "priority": 1 520 | } 521 | ], 522 | "comment_number": 0, 523 | "collect_number": 0, 524 | "source_url": "http://www.youtube.com/watch?v=VjmFKle7xIw", 525 | "translate_status": "translation-not-started" 526 | } 527 | }, 528 | { 529 | "id": 33043, 530 | "index": 2, 531 | "title": null, 532 | "en_title": "How to find a missing angle measure using the law of sines (example)", 533 | "brief": "", 534 | "onshow": true, 535 | "attachment_total": 0, 536 | "attachments": [], 537 | "type": "video", 538 | "resource": { 539 | "id": 19688, 540 | "duration_secs": 333, 541 | "duration_msecs": 333533, 542 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033205462_4653.jpg", 543 | "lecture_id": 33043, 544 | "guid": "4d7f30d9-51d9-40d4-b654-401489a7652d", 545 | "brief": "", 546 | "content": [ 547 | { 548 | "name": "360p", 549 | "url": "http://alicdn.yxgapp.com/miniVideos/4d7f30d9-51d9-40d4-b654-401489a7652d.mp4", 550 | "size": 5009051, 551 | "size_mb": 4.78, 552 | "priority": 1 553 | } 554 | ], 555 | "comment_number": 0, 556 | "collect_number": 0, 557 | "source_url": "http://www.youtube.com/watch?v=IJySBMtFlnQ", 558 | "translate_status": "translation-not-started" 559 | } 560 | }, 561 | { 562 | "id": 33044, 563 | "index": 3, 564 | "title": null, 565 | "en_title": "Proof of the law of sines", 566 | "brief": "", 567 | "onshow": true, 568 | "attachment_total": 0, 569 | "attachments": [], 570 | "type": "video", 571 | "resource": { 572 | "id": 19689, 573 | "duration_secs": 393, 574 | "duration_msecs": 393100, 575 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033224567_8558.jpg", 576 | "lecture_id": 33044, 577 | "guid": "99c5bc4b-266a-46b9-af8e-d60df6d8efe0", 578 | "brief": "", 579 | "content": [ 580 | { 581 | "name": "360p", 582 | "url": "http://alicdn.yxgapp.com/miniVideos/99c5bc4b-266a-46b9-af8e-d60df6d8efe0.mp4", 583 | "size": 6154925, 584 | "size_mb": 5.87, 585 | "priority": 1 586 | } 587 | ], 588 | "comment_number": 0, 589 | "collect_number": 1, 590 | "source_url": "http://www.youtube.com/watch?v=APNkWrD-U1k", 591 | "translate_status": "translation-not-started" 592 | } 593 | }, 594 | { 595 | "id": 33045, 596 | "index": 4, 597 | "title": null, 598 | "en_title": "How to find a missing side length using the law of cosines (example)", 599 | "brief": "", 600 | "onshow": true, 601 | "attachment_total": 0, 602 | "attachments": [], 603 | "type": "video", 604 | "resource": { 605 | "id": 19690, 606 | "duration_secs": 277, 607 | "duration_msecs": 277200, 608 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033239481_9923.jpg", 609 | "lecture_id": 33045, 610 | "guid": "29daab21-a83b-4e06-9172-826295d22081", 611 | "brief": "", 612 | "content": [ 613 | { 614 | "name": "360p", 615 | "url": "http://alicdn.yxgapp.com/miniVideos/29daab21-a83b-4e06-9172-826295d22081.mp4", 616 | "size": 4129527, 617 | "size_mb": 3.94, 618 | "priority": 1 619 | } 620 | ], 621 | "comment_number": 0, 622 | "collect_number": 0, 623 | "source_url": "http://www.youtube.com/watch?v=ZElOxG7_m3c", 624 | "translate_status": "translation-not-started" 625 | } 626 | }, 627 | { 628 | "id": 33046, 629 | "index": 5, 630 | "title": null, 631 | "en_title": "How to find a missing angle measure using the law of cosines (example)", 632 | "brief": "", 633 | "onshow": true, 634 | "attachment_total": 0, 635 | "attachments": [], 636 | "type": "video", 637 | "resource": { 638 | "id": 19691, 639 | "duration_secs": 400, 640 | "duration_msecs": 400800, 641 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033262754_6317.jpg", 642 | "lecture_id": 33046, 643 | "guid": "a6c66c92-8b1c-4907-be37-ddd355e8d1ff", 644 | "brief": "", 645 | "content": [ 646 | { 647 | "name": "360p", 648 | "url": "http://alicdn.yxgapp.com/miniVideos/a6c66c92-8b1c-4907-be37-ddd355e8d1ff.mp4", 649 | "size": 6163427, 650 | "size_mb": 5.88, 651 | "priority": 1 652 | } 653 | ], 654 | "comment_number": 0, 655 | "collect_number": 0, 656 | "source_url": "http://www.youtube.com/watch?v=Ei54NnQ0FKs", 657 | "translate_status": "translation-not-started" 658 | } 659 | }, 660 | { 661 | "id": 33047, 662 | "index": 6, 663 | "title": null, 664 | "en_title": "Proof of the law of cosines", 665 | "brief": "", 666 | "onshow": true, 667 | "attachment_total": 0, 668 | "attachments": [], 669 | "type": "video", 670 | "resource": { 671 | "id": 19692, 672 | "duration_secs": 566, 673 | "duration_msecs": 566600, 674 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033290447_9780.jpg", 675 | "lecture_id": 33047, 676 | "guid": "cd323559-ddc1-48fb-82b3-2f09db617af3", 677 | "brief": "", 678 | "content": [ 679 | { 680 | "name": "360p", 681 | "url": "http://alicdn.yxgapp.com/miniVideos/cd323559-ddc1-48fb-82b3-2f09db617af3.mp4", 682 | "size": 8613797, 683 | "size_mb": 8.21, 684 | "priority": 1 685 | } 686 | ], 687 | "comment_number": 0, 688 | "collect_number": 0, 689 | "source_url": "http://www.youtube.com/watch?v=pGaDcOMdw48", 690 | "translate_status": "translation-not-started" 691 | } 692 | }, 693 | { 694 | "id": 33048, 695 | "index": 7, 696 | "title": null, 697 | "en_title": "Word problem: Solving for a side length in a general triangle (example)", 698 | "brief": "", 699 | "onshow": true, 700 | "attachment_total": 0, 701 | "attachments": [], 702 | "type": "video", 703 | "resource": { 704 | "id": 19693, 705 | "duration_secs": 358, 706 | "duration_msecs": 566600, 707 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033306947_2318.jpg", 708 | "lecture_id": 33048, 709 | "guid": "f8466f8a-10d6-4841-a99e-263513539e15", 710 | "brief": "", 711 | "content": [ 712 | { 713 | "name": "360p", 714 | "url": "http://alicdn.yxgapp.com/miniVideos/f8466f8a-10d6-4841-a99e-263513539e15.mp4", 715 | "size": 8613797, 716 | "size_mb": 8.21, 717 | "priority": 1 718 | } 719 | ], 720 | "comment_number": 0, 721 | "collect_number": 0, 722 | "source_url": "http://www.youtube.com/watch?v=6kGi1dvGZNY", 723 | "translate_status": "translation-not-started" 724 | } 725 | } 726 | ] 727 | }, 728 | { 729 | "id": 2160, 730 | "index": 3, 731 | "title": "正弦,余弦,正切的单位圆定义", 732 | "en_title": "The unit circle definition of sine, cosine, and tangent", 733 | "lectures": [ 734 | { 735 | "id": 33049, 736 | "index": 1, 737 | "title": null, 738 | "en_title": "Introduction to radians", 739 | "brief": "", 740 | "onshow": true, 741 | "attachment_total": 0, 742 | "attachments": [], 743 | "type": "video", 744 | "resource": { 745 | "id": 19694, 746 | "duration_secs": 649, 747 | "duration_msecs": 649867, 748 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033339018_9002.jpg", 749 | "lecture_id": 33049, 750 | "guid": "4f3c2da4-99d3-4aa8-9381-8d69ebc84870", 751 | "brief": "", 752 | "content": [ 753 | { 754 | "name": "360p", 755 | "url": "http://alicdn.yxgapp.com/miniVideos/4f3c2da4-99d3-4aa8-9381-8d69ebc84870.mp4", 756 | "size": 9588065, 757 | "size_mb": 9.14, 758 | "priority": 1 759 | } 760 | ], 761 | "comment_number": 0, 762 | "collect_number": 0, 763 | "source_url": "http://www.youtube.com/watch?v=EnwWxMZVBeg", 764 | "translate_status": "translation-not-started" 765 | } 766 | }, 767 | { 768 | "id": 33050, 769 | "index": 2, 770 | "title": null, 771 | "en_title": "Converting between radians and degrees", 772 | "brief": "", 773 | "onshow": true, 774 | "attachment_total": 0, 775 | "attachments": [], 776 | "type": "video", 777 | "resource": { 778 | "id": 19695, 779 | "duration_secs": 430, 780 | "duration_msecs": 430867, 781 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033359013_2126.jpg", 782 | "lecture_id": 33050, 783 | "guid": "2231eea7-405b-45af-8f1f-d270bf7069ad", 784 | "brief": "", 785 | "content": [ 786 | { 787 | "name": "360p", 788 | "url": "http://alicdn.yxgapp.com/miniVideos/2231eea7-405b-45af-8f1f-d270bf7069ad.mp4", 789 | "size": 6278618, 790 | "size_mb": 5.99, 791 | "priority": 1 792 | } 793 | ], 794 | "comment_number": 0, 795 | "collect_number": 0, 796 | "source_url": "http://www.youtube.com/watch?v=z8vj8tUCkxY", 797 | "translate_status": "translation-not-started" 798 | } 799 | }, 800 | { 801 | "id": 33051, 802 | "index": 3, 803 | "title": null, 804 | "en_title": "How to convert from degrees to radians (example)", 805 | "brief": "", 806 | "onshow": true, 807 | "attachment_total": 0, 808 | "attachments": [], 809 | "type": "video", 810 | "resource": { 811 | "id": 19696, 812 | "duration_secs": 420, 813 | "duration_msecs": 420867, 814 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033382620_1490.jpg", 815 | "lecture_id": 33051, 816 | "guid": "20017217-ee97-4ad7-9cbb-1a6df4362b46", 817 | "brief": "", 818 | "content": [ 819 | { 820 | "name": "360p", 821 | "url": "http://alicdn.yxgapp.com/miniVideos/20017217-ee97-4ad7-9cbb-1a6df4362b46.mp4", 822 | "size": 6273133, 823 | "size_mb": 5.98, 824 | "priority": 1 825 | } 826 | ], 827 | "comment_number": 0, 828 | "collect_number": 0, 829 | "source_url": "http://www.youtube.com/watch?v=O3jvUZ8wvZs", 830 | "translate_status": "translation-not-started" 831 | } 832 | }, 833 | { 834 | "id": 33052, 835 | "index": 4, 836 | "title": null, 837 | "en_title": "How to convert from radians to degrees (example)", 838 | "brief": "", 839 | "onshow": true, 840 | "attachment_total": 0, 841 | "attachments": [], 842 | "type": "video", 843 | "resource": { 844 | "id": 19697, 845 | "duration_secs": 199, 846 | "duration_msecs": 420867, 847 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033392911_8512.jpg", 848 | "lecture_id": 33052, 849 | "guid": "ecac6f79-3023-40f3-b4c6-a7b34c97d01e", 850 | "brief": "", 851 | "content": [ 852 | { 853 | "name": "360p", 854 | "url": "http://alicdn.yxgapp.com/miniVideos/ecac6f79-3023-40f3-b4c6-a7b34c97d01e.mp4", 855 | "size": 6273133, 856 | "size_mb": 5.98, 857 | "priority": 1 858 | } 859 | ], 860 | "comment_number": 0, 861 | "collect_number": 0, 862 | "source_url": "http://www.youtube.com/watch?v=z0-1gBy1ykE", 863 | "translate_status": "translation-not-started" 864 | } 865 | }, 866 | { 867 | "id": 33053, 868 | "index": 5, 869 | "title": null, 870 | "en_title": "Rotation by radians and quadrants", 871 | "brief": "", 872 | "onshow": true, 873 | "attachment_total": 0, 874 | "attachments": [], 875 | "type": "video", 876 | "resource": { 877 | "id": 19698, 878 | "duration_secs": 230, 879 | "duration_msecs": 230000, 880 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033417537_9968.jpg", 881 | "lecture_id": 33053, 882 | "guid": "00d9393c-8fdb-4e1c-a3af-703751e8f6dd", 883 | "brief": "", 884 | "content": [ 885 | { 886 | "name": "360p", 887 | "url": "http://alicdn.yxgapp.com/miniVideos/00d9393c-8fdb-4e1c-a3af-703751e8f6dd.mp4", 888 | "size": 3789947, 889 | "size_mb": 3.61, 890 | "priority": 1 891 | } 892 | ], 893 | "comment_number": 0, 894 | "collect_number": 0, 895 | "source_url": "http://www.youtube.com/watch?v=fYQ3GRSu4JU", 896 | "translate_status": "translation-not-started" 897 | } 898 | }, 899 | { 900 | "id": 33054, 901 | "index": 6, 902 | "title": null, 903 | "en_title": "The unit circle definition of sine, cosine, and tangent", 904 | "brief": "", 905 | "onshow": true, 906 | "attachment_total": 0, 907 | "attachments": [], 908 | "type": "video", 909 | "resource": { 910 | "id": 19699, 911 | "duration_secs": 543, 912 | "duration_msecs": 543667, 913 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033437771_2818.jpg", 914 | "lecture_id": 33054, 915 | "guid": "9b08e311-9491-4060-91a3-963b01d1a0a3", 916 | "brief": "", 917 | "content": [ 918 | { 919 | "name": "360p", 920 | "url": "http://alicdn.yxgapp.com/miniVideos/9b08e311-9491-4060-91a3-963b01d1a0a3.mp4", 921 | "size": 8066443, 922 | "size_mb": 7.69, 923 | "priority": 1 924 | } 925 | ], 926 | "comment_number": 0, 927 | "collect_number": 0, 928 | "source_url": "http://www.youtube.com/watch?v=1m9p9iubMLU", 929 | "translate_status": "translation-not-started" 930 | } 931 | }, 932 | { 933 | "id": 33055, 934 | "index": 7, 935 | "title": null, 936 | "en_title": "How to find trigonometric values using the unit circle definition (example)", 937 | "brief": "", 938 | "onshow": true, 939 | "attachment_total": 0, 940 | "attachments": [], 941 | "type": "video", 942 | "resource": { 943 | "id": 19700, 944 | "duration_secs": 251, 945 | "duration_msecs": 543667, 946 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033452496_4586.jpg", 947 | "lecture_id": 33055, 948 | "guid": "efd26412-5a9e-4109-8c5b-a41c13570590", 949 | "brief": "", 950 | "content": [ 951 | { 952 | "name": "360p", 953 | "url": "http://alicdn.yxgapp.com/miniVideos/efd26412-5a9e-4109-8c5b-a41c13570590.mp4", 954 | "size": 8066443, 955 | "size_mb": 7.69, 956 | "priority": 1 957 | } 958 | ], 959 | "comment_number": 0, 960 | "collect_number": 0, 961 | "source_url": "http://www.youtube.com/watch?v=Jni7E2RH43s", 962 | "translate_status": "translation-not-started" 963 | } 964 | }, 965 | { 966 | "id": 33056, 967 | "index": 8, 968 | "title": null, 969 | "en_title": "Matching the SOH CAH TOA and the unit circle definitions of the trigonometric functions", 970 | "brief": "", 971 | "onshow": true, 972 | "attachment_total": 0, 973 | "attachments": [], 974 | "type": "video", 975 | "resource": { 976 | "id": 19701, 977 | "duration_secs": 416, 978 | "duration_msecs": 416267, 979 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033475745_5416.jpg", 980 | "lecture_id": 33056, 981 | "guid": "aff76c2e-72f3-4e8f-984b-08a48937ff05", 982 | "brief": "", 983 | "content": [ 984 | { 985 | "name": "360p", 986 | "url": "http://alicdn.yxgapp.com/miniVideos/aff76c2e-72f3-4e8f-984b-08a48937ff05.mp4", 987 | "size": 6402968, 988 | "size_mb": 6.11, 989 | "priority": 1 990 | } 991 | ], 992 | "comment_number": 0, 993 | "collect_number": 0, 994 | "source_url": "http://www.youtube.com/watch?v=WffVdYETdng", 995 | "translate_status": "translation-not-started" 996 | } 997 | }, 998 | { 999 | "id": 33057, 1000 | "index": 9, 1001 | "title": null, 1002 | "en_title": "The graph of the sine function", 1003 | "brief": "", 1004 | "onshow": true, 1005 | "attachment_total": 0, 1006 | "attachments": [], 1007 | "type": "video", 1008 | "resource": { 1009 | "id": 19702, 1010 | "duration_secs": 561, 1011 | "duration_msecs": 561333, 1012 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033504401_7370.jpg", 1013 | "lecture_id": 33057, 1014 | "guid": "8686b92b-dd68-4a10-96e9-a0ae26915e7a", 1015 | "brief": "", 1016 | "content": [ 1017 | { 1018 | "name": "360p", 1019 | "url": "http://alicdn.yxgapp.com/miniVideos/8686b92b-dd68-4a10-96e9-a0ae26915e7a.mp4", 1020 | "size": 8237766, 1021 | "size_mb": 7.86, 1022 | "priority": 1 1023 | } 1024 | ], 1025 | "comment_number": 0, 1026 | "collect_number": 0, 1027 | "source_url": "http://www.youtube.com/watch?v=sjUhr0HkLUg", 1028 | "translate_status": "translation-not-started" 1029 | } 1030 | }, 1031 | { 1032 | "id": 33058, 1033 | "index": 10, 1034 | "title": null, 1035 | "en_title": "The graph of the tangent function", 1036 | "brief": "", 1037 | "onshow": true, 1038 | "attachment_total": 0, 1039 | "attachments": [], 1040 | "type": "video", 1041 | "resource": { 1042 | "id": 19703, 1043 | "duration_secs": 611, 1044 | "duration_msecs": 611267, 1045 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033542340_45.jpg", 1046 | "lecture_id": 33058, 1047 | "guid": "30c2ffb9-54de-4d4d-8750-830c078cb966", 1048 | "brief": "", 1049 | "content": [ 1050 | { 1051 | "name": "360p", 1052 | "url": "http://alicdn.yxgapp.com/miniVideos/30c2ffb9-54de-4d4d-8750-830c078cb966.mp4", 1053 | "size": 9429646, 1054 | "size_mb": 8.99, 1055 | "priority": 1 1056 | } 1057 | ], 1058 | "comment_number": 0, 1059 | "collect_number": 0, 1060 | "source_url": "http://www.youtube.com/watch?v=FK6-tZ5D7xM", 1061 | "translate_status": "translation-not-started" 1062 | } 1063 | }, 1064 | { 1065 | "id": 33059, 1066 | "index": 11, 1067 | "title": null, 1068 | "en_title": "Finding the intersection points of the graphs of sine and cosine", 1069 | "brief": "", 1070 | "onshow": true, 1071 | "attachment_total": 0, 1072 | "attachments": [], 1073 | "type": "video", 1074 | "resource": { 1075 | "id": 19704, 1076 | "duration_secs": 666, 1077 | "duration_msecs": 666600, 1078 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033575878_4859.jpg", 1079 | "lecture_id": 33059, 1080 | "guid": "13de3c73-2ebe-49ec-ab3b-b4bdae31e884", 1081 | "brief": "", 1082 | "content": [ 1083 | { 1084 | "name": "360p", 1085 | "url": "http://alicdn.yxgapp.com/miniVideos/13de3c73-2ebe-49ec-ab3b-b4bdae31e884.mp4", 1086 | "size": 10343183, 1087 | "size_mb": 9.86, 1088 | "priority": 1 1089 | } 1090 | ], 1091 | "comment_number": 0, 1092 | "collect_number": 0, 1093 | "source_url": "http://www.youtube.com/watch?v=fp9DZYmiSC4", 1094 | "translate_status": "translation-not-started" 1095 | } 1096 | }, 1097 | { 1098 | "id": 33060, 1099 | "index": 12, 1100 | "title": null, 1101 | "en_title": "Sine and cosine identities that arise from the symmetry of the unit circle", 1102 | "brief": "", 1103 | "onshow": true, 1104 | "attachment_total": 0, 1105 | "attachments": [], 1106 | "type": "video", 1107 | "resource": { 1108 | "id": 19705, 1109 | "duration_secs": 477, 1110 | "duration_msecs": 477733, 1111 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033598746_461.jpg", 1112 | "lecture_id": 33060, 1113 | "guid": "01aaec42-bdcc-48fc-afe9-f49034203a3c", 1114 | "brief": "", 1115 | "content": [ 1116 | { 1117 | "name": "360p", 1118 | "url": "http://alicdn.yxgapp.com/miniVideos/01aaec42-bdcc-48fc-afe9-f49034203a3c.mp4", 1119 | "size": 7229621, 1120 | "size_mb": 6.89, 1121 | "priority": 1 1122 | } 1123 | ], 1124 | "comment_number": 0, 1125 | "collect_number": 0, 1126 | "source_url": "http://www.youtube.com/watch?v=tzQ7arA917E", 1127 | "translate_status": "translation-not-started" 1128 | } 1129 | }, 1130 | { 1131 | "id": 33061, 1132 | "index": 13, 1133 | "title": null, 1134 | "en_title": "Tangent identities that arise from the symmetry of the unit circle", 1135 | "brief": "", 1136 | "onshow": true, 1137 | "attachment_total": 0, 1138 | "attachments": [], 1139 | "type": "video", 1140 | "resource": { 1141 | "id": 19706, 1142 | "duration_secs": 434, 1143 | "duration_msecs": 434600, 1144 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033625414_5262.jpg", 1145 | "lecture_id": 33061, 1146 | "guid": "066fe79d-b543-4847-894c-4eb819cdb062", 1147 | "brief": "", 1148 | "content": [ 1149 | { 1150 | "name": "360p", 1151 | "url": "http://alicdn.yxgapp.com/miniVideos/066fe79d-b543-4847-894c-4eb819cdb062.mp4", 1152 | "size": 7941914, 1153 | "size_mb": 7.57, 1154 | "priority": 1 1155 | } 1156 | ], 1157 | "comment_number": 0, 1158 | "collect_number": 0, 1159 | "source_url": "http://www.youtube.com/watch?v=k_wJsio68D4", 1160 | "translate_status": "translation-not-started" 1161 | } 1162 | }, 1163 | { 1164 | "id": 33062, 1165 | "index": 14, 1166 | "title": null, 1167 | "en_title": "Sine and cosine identities that arise from angle rotations on the unit circle", 1168 | "brief": "", 1169 | "onshow": true, 1170 | "attachment_total": 0, 1171 | "attachments": [], 1172 | "type": "video", 1173 | "resource": { 1174 | "id": 19707, 1175 | "duration_secs": 372, 1176 | "duration_msecs": 372667, 1177 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033642619_7145.jpg", 1178 | "lecture_id": 33062, 1179 | "guid": "b29f068b-b4ba-45c9-9821-78bff3633f9f", 1180 | "brief": "", 1181 | "content": [ 1182 | { 1183 | "name": "360p", 1184 | "url": "http://alicdn.yxgapp.com/miniVideos/b29f068b-b4ba-45c9-9821-78bff3633f9f.mp4", 1185 | "size": 5524294, 1186 | "size_mb": 5.27, 1187 | "priority": 1 1188 | } 1189 | ], 1190 | "comment_number": 0, 1191 | "collect_number": 0, 1192 | "source_url": "http://www.youtube.com/watch?v=h-TPSylHrvE", 1193 | "translate_status": "translation-not-started" 1194 | } 1195 | }, 1196 | { 1197 | "id": 33063, 1198 | "index": 15, 1199 | "title": null, 1200 | "en_title": "Identity that arises from the periodicity of the tangent function", 1201 | "brief": "", 1202 | "onshow": true, 1203 | "attachment_total": 0, 1204 | "attachments": [], 1205 | "type": "video", 1206 | "resource": { 1207 | "id": 19708, 1208 | "duration_secs": 241, 1209 | "duration_msecs": 372667, 1210 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033655804_3348.jpg", 1211 | "lecture_id": 33063, 1212 | "guid": "f506a1db-1a5f-42b7-a417-3d534438d7b6", 1213 | "brief": "", 1214 | "content": [ 1215 | { 1216 | "name": "360p", 1217 | "url": "http://alicdn.yxgapp.com/miniVideos/f506a1db-1a5f-42b7-a417-3d534438d7b6.mp4", 1218 | "size": 5524294, 1219 | "size_mb": 5.27, 1220 | "priority": 1 1221 | } 1222 | ], 1223 | "comment_number": 0, 1224 | "collect_number": 0, 1225 | "source_url": "http://www.youtube.com/watch?v=C3HFAyigqoY", 1226 | "translate_status": "translation-not-started" 1227 | } 1228 | }, 1229 | { 1230 | "id": 33064, 1231 | "index": 16, 1232 | "title": null, 1233 | "en_title": "Finding the trigonometric values of a special angle in the unit circle", 1234 | "brief": "", 1235 | "onshow": true, 1236 | "attachment_total": 0, 1237 | "attachments": [], 1238 | "type": "video", 1239 | "resource": { 1240 | "id": 19709, 1241 | "duration_secs": 472, 1242 | "duration_msecs": 372667, 1243 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033683697_7050.jpg", 1244 | "lecture_id": 33064, 1245 | "guid": "fafa6dca-a149-4d46-95c3-ec571a0bcfe9", 1246 | "brief": "", 1247 | "content": [ 1248 | { 1249 | "name": "360p", 1250 | "url": "http://alicdn.yxgapp.com/miniVideos/fafa6dca-a149-4d46-95c3-ec571a0bcfe9.mp4", 1251 | "size": 5524294, 1252 | "size_mb": 5.27, 1253 | "priority": 1 1254 | } 1255 | ], 1256 | "comment_number": 0, 1257 | "collect_number": 0, 1258 | "source_url": "http://www.youtube.com/watch?v=KoYZErFpZ5Q", 1259 | "translate_status": "translation-not-started" 1260 | } 1261 | }, 1262 | { 1263 | "id": 33065, 1264 | "index": 17, 1265 | "title": null, 1266 | "en_title": "Proof of the Pythagorean trigonometric identity", 1267 | "brief": "", 1268 | "onshow": true, 1269 | "attachment_total": 0, 1270 | "attachments": [], 1271 | "type": "video", 1272 | "resource": { 1273 | "id": 19710, 1274 | "duration_secs": 371, 1275 | "duration_msecs": 371333, 1276 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033700206_4820.jpg", 1277 | "lecture_id": 33065, 1278 | "guid": "dcb4ad70-61e3-40b2-afa5-e84efa008cf9", 1279 | "brief": "", 1280 | "content": [ 1281 | { 1282 | "name": "360p", 1283 | "url": "http://alicdn.yxgapp.com/miniVideos/dcb4ad70-61e3-40b2-afa5-e84efa008cf9.mp4", 1284 | "size": 5388491, 1285 | "size_mb": 5.14, 1286 | "priority": 1 1287 | } 1288 | ], 1289 | "comment_number": 0, 1290 | "collect_number": 0, 1291 | "source_url": "http://www.youtube.com/watch?v=n0DLSIOYBsQ", 1292 | "translate_status": "translation-not-started" 1293 | } 1294 | }, 1295 | { 1296 | "id": 33066, 1297 | "index": 18, 1298 | "title": null, 1299 | "en_title": "How to find a trig value of an angle given another trig value of the angle (example)", 1300 | "brief": "", 1301 | "onshow": true, 1302 | "attachment_total": 0, 1303 | "attachments": [], 1304 | "type": "video", 1305 | "resource": { 1306 | "id": 19711, 1307 | "duration_secs": 374, 1308 | "duration_msecs": 374667, 1309 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033718055_4288.jpg", 1310 | "lecture_id": 33066, 1311 | "guid": "4ed3a5ee-3e33-4981-b94d-c431df654c18", 1312 | "brief": "", 1313 | "content": [ 1314 | { 1315 | "name": "360p", 1316 | "url": "http://alicdn.yxgapp.com/miniVideos/4ed3a5ee-3e33-4981-b94d-c431df654c18.mp4", 1317 | "size": 5508869, 1318 | "size_mb": 5.25, 1319 | "priority": 1 1320 | } 1321 | ], 1322 | "comment_number": 0, 1323 | "collect_number": 0, 1324 | "source_url": "http://www.youtube.com/watch?v=soIt2TwV6Xk", 1325 | "translate_status": "translation-not-started" 1326 | } 1327 | }, 1328 | { 1329 | "id": 33067, 1330 | "index": 19, 1331 | "title": null, 1332 | "en_title": "Tau versus pi", 1333 | "brief": "", 1334 | "onshow": true, 1335 | "attachment_total": 0, 1336 | "attachments": [], 1337 | "type": "video", 1338 | "resource": { 1339 | "id": 19712, 1340 | "duration_secs": 981, 1341 | "duration_msecs": 374667, 1342 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033772233_4977.jpg", 1343 | "lecture_id": 33067, 1344 | "guid": "ede256ac-0f0b-4135-8e3c-76b2b7ed4195", 1345 | "brief": "", 1346 | "content": [ 1347 | { 1348 | "name": "360p", 1349 | "url": "http://alicdn.yxgapp.com/miniVideos/ede256ac-0f0b-4135-8e3c-76b2b7ed4195.mp4", 1350 | "size": 5508869, 1351 | "size_mb": 5.25, 1352 | "priority": 1 1353 | } 1354 | ], 1355 | "comment_number": 0, 1356 | "collect_number": 0, 1357 | "source_url": "http://www.youtube.com/watch?v=1jDDfkKKgmc", 1358 | "translate_status": "translation-not-started" 1359 | } 1360 | }, 1361 | { 1362 | "id": 33068, 1363 | "index": 20, 1364 | "title": null, 1365 | "en_title": "Pi is (still) wrong", 1366 | "brief": "", 1367 | "onshow": true, 1368 | "attachment_total": 0, 1369 | "attachments": [], 1370 | "type": "video", 1371 | "resource": { 1372 | "id": 19713, 1373 | "duration_secs": 316, 1374 | "duration_msecs": 316200, 1375 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033857259_2891.jpg", 1376 | "lecture_id": 33068, 1377 | "guid": "488cba5b-62b3-45c8-91ce-5243f96e2bd7", 1378 | "brief": "", 1379 | "content": [ 1380 | { 1381 | "name": "360p", 1382 | "url": "http://alicdn.yxgapp.com/miniVideos/488cba5b-62b3-45c8-91ce-5243f96e2bd7.mp4", 1383 | "size": 27388802, 1384 | "size_mb": 26.12, 1385 | "priority": 1 1386 | } 1387 | ], 1388 | "comment_number": 0, 1389 | "collect_number": 0, 1390 | "source_url": "http://www.youtube.com/watch?v=jG7vhMMXagQ", 1391 | "translate_status": "translation-not-started" 1392 | } 1393 | } 1394 | ] 1395 | }, 1396 | { 1397 | "id": 2161, 1398 | "index": 4, 1399 | "title": "三角函数曲线", 1400 | "en_title": "Graphs of trigonometric functions", 1401 | "lectures": [ 1402 | { 1403 | "id": 33069, 1404 | "index": 1, 1405 | "title": null, 1406 | "en_title": "The graph of the sine function", 1407 | "brief": "", 1408 | "onshow": true, 1409 | "attachment_total": 0, 1410 | "attachments": [], 1411 | "type": "video", 1412 | "resource": { 1413 | "id": 19714, 1414 | "duration_secs": 561, 1415 | "duration_msecs": 561333, 1416 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033876277_6635.jpg", 1417 | "lecture_id": 33069, 1418 | "guid": "17cc8f85-552c-471b-ad42-7120d8881e09", 1419 | "brief": "", 1420 | "content": [ 1421 | { 1422 | "name": "360p", 1423 | "url": "http://alicdn.yxgapp.com/miniVideos/17cc8f85-552c-471b-ad42-7120d8881e09.mp4", 1424 | "size": 8237766, 1425 | "size_mb": 7.86, 1426 | "priority": 1 1427 | } 1428 | ], 1429 | "comment_number": 0, 1430 | "collect_number": 0, 1431 | "source_url": "http://www.youtube.com/watch?v=sjUhr0HkLUg", 1432 | "translate_status": "translation-not-started" 1433 | } 1434 | }, 1435 | { 1436 | "id": 33070, 1437 | "index": 2, 1438 | "title": null, 1439 | "en_title": "The graph of the tangent function", 1440 | "brief": "", 1441 | "onshow": true, 1442 | "attachment_total": 0, 1443 | "attachments": [], 1444 | "type": "video", 1445 | "resource": { 1446 | "id": 19715, 1447 | "duration_secs": 611, 1448 | "duration_msecs": 561333, 1449 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033912299_4023.jpg", 1450 | "lecture_id": 33070, 1451 | "guid": "f55c83d9-7adb-4dee-b014-08874d700ab5", 1452 | "brief": "", 1453 | "content": [ 1454 | { 1455 | "name": "360p", 1456 | "url": "http://alicdn.yxgapp.com/miniVideos/f55c83d9-7adb-4dee-b014-08874d700ab5.mp4", 1457 | "size": 8237766, 1458 | "size_mb": 7.86, 1459 | "priority": 1 1460 | } 1461 | ], 1462 | "comment_number": 0, 1463 | "collect_number": 0, 1464 | "source_url": "http://www.youtube.com/watch?v=FK6-tZ5D7xM", 1465 | "translate_status": "translation-not-started" 1466 | } 1467 | }, 1468 | { 1469 | "id": 33071, 1470 | "index": 3, 1471 | "title": null, 1472 | "en_title": "Finding the intersection points of the graphs of sine and cosine", 1473 | "brief": "", 1474 | "onshow": true, 1475 | "attachment_total": 0, 1476 | "attachments": [], 1477 | "type": "video", 1478 | "resource": { 1479 | "id": 19716, 1480 | "duration_secs": 666, 1481 | "duration_msecs": 666600, 1482 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033947143_7617.jpg", 1483 | "lecture_id": 33071, 1484 | "guid": "0bb8015a-1d12-4cdb-b31f-8ff2afb3b5da", 1485 | "brief": "", 1486 | "content": [ 1487 | { 1488 | "name": "360p", 1489 | "url": "http://alicdn.yxgapp.com/miniVideos/0bb8015a-1d12-4cdb-b31f-8ff2afb3b5da.mp4", 1490 | "size": 10343183, 1491 | "size_mb": 9.86, 1492 | "priority": 1 1493 | } 1494 | ], 1495 | "comment_number": 0, 1496 | "collect_number": 0, 1497 | "source_url": "http://www.youtube.com/watch?v=fp9DZYmiSC4", 1498 | "translate_status": "translation-not-started" 1499 | } 1500 | }, 1501 | { 1502 | "id": 33072, 1503 | "index": 4, 1504 | "title": null, 1505 | "en_title": "Midline, amplitude and period of a sinusoidal function", 1506 | "brief": "", 1507 | "onshow": true, 1508 | "attachment_total": 0, 1509 | "attachments": [], 1510 | "type": "video", 1511 | "resource": { 1512 | "id": 19717, 1513 | "duration_secs": 297, 1514 | "duration_msecs": 297667, 1515 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033962837_4659.jpg", 1516 | "lecture_id": 33072, 1517 | "guid": "e1f96f8e-d075-4c4a-ad88-4cb21e30167e", 1518 | "brief": "", 1519 | "content": [ 1520 | { 1521 | "name": "360p", 1522 | "url": "http://alicdn.yxgapp.com/miniVideos/e1f96f8e-d075-4c4a-ad88-4cb21e30167e.mp4", 1523 | "size": 4626551, 1524 | "size_mb": 4.41, 1525 | "priority": 1 1526 | } 1527 | ], 1528 | "comment_number": 0, 1529 | "collect_number": 0, 1530 | "source_url": "http://www.youtube.com/watch?v=s4cLM0l1gd4", 1531 | "translate_status": "translation-not-started" 1532 | } 1533 | }, 1534 | { 1535 | "id": 33073, 1536 | "index": 5, 1537 | "title": null, 1538 | "en_title": "How to find the amplitude and period of a sinusoidal functions from its formula (example)", 1539 | "brief": "", 1540 | "onshow": true, 1541 | "attachment_total": 0, 1542 | "attachments": [], 1543 | "type": "video", 1544 | "resource": { 1545 | "id": 19718, 1546 | "duration_secs": 500, 1547 | "duration_msecs": 500800, 1548 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457033988826_230.jpg", 1549 | "lecture_id": 33073, 1550 | "guid": "3235af6b-1692-4f50-85ed-b3ea374c65a5", 1551 | "brief": "", 1552 | "content": [ 1553 | { 1554 | "name": "360p", 1555 | "url": "http://alicdn.yxgapp.com/miniVideos/3235af6b-1692-4f50-85ed-b3ea374c65a5.mp4", 1556 | "size": 7454527, 1557 | "size_mb": 7.11, 1558 | "priority": 1 1559 | } 1560 | ], 1561 | "comment_number": 0, 1562 | "collect_number": 0, 1563 | "source_url": "http://www.youtube.com/watch?v=SBqnRja4CW4", 1564 | "translate_status": "translation-not-started" 1565 | } 1566 | }, 1567 | { 1568 | "id": 33074, 1569 | "index": 6, 1570 | "title": null, 1571 | "en_title": "How to find the amplitude and period of a sinusoidal functions from its formula (example)", 1572 | "brief": "", 1573 | "onshow": true, 1574 | "attachment_total": 0, 1575 | "attachments": [], 1576 | "type": "video", 1577 | "resource": { 1578 | "id": 19719, 1579 | "duration_secs": 500, 1580 | "duration_msecs": 500800, 1581 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034021480_3068.jpg", 1582 | "lecture_id": 33074, 1583 | "guid": "37b694ab-b16c-4499-8ae0-a6a6dd5e03cc", 1584 | "brief": "", 1585 | "content": [ 1586 | { 1587 | "name": "360p", 1588 | "url": "http://alicdn.yxgapp.com/miniVideos/37b694ab-b16c-4499-8ae0-a6a6dd5e03cc.mp4", 1589 | "size": 7454527, 1590 | "size_mb": 7.11, 1591 | "priority": 1 1592 | } 1593 | ], 1594 | "comment_number": 0, 1595 | "collect_number": 0, 1596 | "source_url": "http://www.youtube.com/watch?v=SBqnRja4CW4", 1597 | "translate_status": "translation-not-started" 1598 | } 1599 | }, 1600 | { 1601 | "id": 33075, 1602 | "index": 7, 1603 | "title": null, 1604 | "en_title": "Graph of a sinusoidal function with vertical stretch and horizontal reflection", 1605 | "brief": "", 1606 | "onshow": true, 1607 | "attachment_total": 0, 1608 | "attachments": [], 1609 | "type": "video", 1610 | "resource": { 1611 | "id": 19720, 1612 | "duration_secs": 771, 1613 | "duration_msecs": 771333, 1614 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034052731_6344.jpg", 1615 | "lecture_id": 33075, 1616 | "guid": "a225b9b9-0f37-4c42-9fec-d12206dc939b", 1617 | "brief": "", 1618 | "content": [ 1619 | { 1620 | "name": "360p", 1621 | "url": "http://alicdn.yxgapp.com/miniVideos/a225b9b9-0f37-4c42-9fec-d12206dc939b.mp4", 1622 | "size": 11783998, 1623 | "size_mb": 11.24, 1624 | "priority": 1 1625 | } 1626 | ], 1627 | "comment_number": 0, 1628 | "collect_number": 0, 1629 | "source_url": "http://www.youtube.com/watch?v=0zCcFSO8ouE", 1630 | "translate_status": "translation-not-started" 1631 | } 1632 | }, 1633 | { 1634 | "id": 33076, 1635 | "index": 8, 1636 | "title": null, 1637 | "en_title": "Graph of a sinusoidal function with vertical and horizontal stretches", 1638 | "brief": "", 1639 | "onshow": true, 1640 | "attachment_total": 0, 1641 | "attachments": [], 1642 | "type": "video", 1643 | "resource": { 1644 | "id": 19721, 1645 | "duration_secs": 642, 1646 | "duration_msecs": 642533, 1647 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034089869_6070.jpg", 1648 | "lecture_id": 33076, 1649 | "guid": "73d48bf7-9c01-42e2-bf53-9f5ba318f1e5", 1650 | "brief": "", 1651 | "content": [ 1652 | { 1653 | "name": "360p", 1654 | "url": "http://alicdn.yxgapp.com/miniVideos/73d48bf7-9c01-42e2-bf53-9f5ba318f1e5.mp4", 1655 | "size": 10043420, 1656 | "size_mb": 9.58, 1657 | "priority": 1 1658 | } 1659 | ], 1660 | "comment_number": 0, 1661 | "collect_number": 0, 1662 | "source_url": "http://www.youtube.com/watch?v=uBVhtGL9y88", 1663 | "translate_status": "translation-not-started" 1664 | } 1665 | }, 1666 | { 1667 | "id": 33077, 1668 | "index": 9, 1669 | "title": null, 1670 | "en_title": "How to construct a sinusoidal function from its graph", 1671 | "brief": "", 1672 | "onshow": true, 1673 | "attachment_total": 0, 1674 | "attachments": [], 1675 | "type": "video", 1676 | "resource": { 1677 | "id": 19722, 1678 | "duration_secs": 321, 1679 | "duration_msecs": 321000, 1680 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034106745_203.jpg", 1681 | "lecture_id": 33077, 1682 | "guid": "93890e1e-3d60-4a31-9a47-0c97a2362f2f", 1683 | "brief": "", 1684 | "content": [ 1685 | { 1686 | "name": "360p", 1687 | "url": "http://alicdn.yxgapp.com/miniVideos/93890e1e-3d60-4a31-9a47-0c97a2362f2f.mp4", 1688 | "size": 4909423, 1689 | "size_mb": 4.68, 1690 | "priority": 1 1691 | } 1692 | ], 1693 | "comment_number": 0, 1694 | "collect_number": 0, 1695 | "source_url": "http://www.youtube.com/watch?v=yHo0CcDVHsk", 1696 | "translate_status": "translation-not-started" 1697 | } 1698 | }, 1699 | { 1700 | "id": 33078, 1701 | "index": 10, 1702 | "title": null, 1703 | "en_title": "Word problem: Constructing a sinusoidal model using sine", 1704 | "brief": "", 1705 | "onshow": true, 1706 | "attachment_total": 0, 1707 | "attachments": [], 1708 | "type": "video", 1709 | "resource": { 1710 | "id": 19723, 1711 | "duration_secs": 654, 1712 | "duration_msecs": 654667, 1713 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034142668_9116.jpg", 1714 | "lecture_id": 33078, 1715 | "guid": "e0867374-100c-4c50-b6be-49f855df0d23", 1716 | "brief": "", 1717 | "content": [ 1718 | { 1719 | "name": "360p", 1720 | "url": "http://alicdn.yxgapp.com/miniVideos/e0867374-100c-4c50-b6be-49f855df0d23.mp4", 1721 | "size": 10030816, 1722 | "size_mb": 9.57, 1723 | "priority": 1 1724 | } 1725 | ], 1726 | "comment_number": 0, 1727 | "collect_number": 0, 1728 | "source_url": "http://www.youtube.com/watch?v=RX0DY9eRp8g", 1729 | "translate_status": "translation-not-started" 1730 | } 1731 | }, 1732 | { 1733 | "id": 33079, 1734 | "index": 11, 1735 | "title": null, 1736 | "en_title": "Word problem: Constructing a sinusoidal model using cosine", 1737 | "brief": "", 1738 | "onshow": true, 1739 | "attachment_total": 0, 1740 | "attachments": [], 1741 | "type": "video", 1742 | "resource": { 1743 | "id": 19724, 1744 | "duration_secs": 428, 1745 | "duration_msecs": 428467, 1746 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034163553_5170.jpg", 1747 | "lecture_id": 33079, 1748 | "guid": "7c5596fd-5586-4812-a6ae-712f20fd0e95", 1749 | "brief": "", 1750 | "content": [ 1751 | { 1752 | "name": "360p", 1753 | "url": "http://alicdn.yxgapp.com/miniVideos/7c5596fd-5586-4812-a6ae-712f20fd0e95.mp4", 1754 | "size": 6469635, 1755 | "size_mb": 6.17, 1756 | "priority": 1 1757 | } 1758 | ], 1759 | "comment_number": 0, 1760 | "collect_number": 0, 1761 | "source_url": "http://www.youtube.com/watch?v=mVlCXkht6hg", 1762 | "translate_status": "translation-not-started" 1763 | } 1764 | }, 1765 | { 1766 | "id": 33080, 1767 | "index": 12, 1768 | "title": null, 1769 | "en_title": "Word problem: Constructing a sinusoidal model with phase shift", 1770 | "brief": "", 1771 | "onshow": true, 1772 | "attachment_total": 0, 1773 | "attachments": [], 1774 | "type": "video", 1775 | "resource": { 1776 | "id": 19725, 1777 | "duration_secs": 462, 1778 | "duration_msecs": 462133, 1779 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034189409_9719.jpg", 1780 | "lecture_id": 33080, 1781 | "guid": "a108eb87-31d8-4f2e-aff0-ee7a08c9f5e1", 1782 | "brief": "", 1783 | "content": [ 1784 | { 1785 | "name": "360p", 1786 | "url": "http://alicdn.yxgapp.com/miniVideos/a108eb87-31d8-4f2e-aff0-ee7a08c9f5e1.mp4", 1787 | "size": 7295818, 1788 | "size_mb": 6.96, 1789 | "priority": 1 1790 | } 1791 | ], 1792 | "comment_number": 0, 1793 | "collect_number": 0, 1794 | "source_url": "http://www.youtube.com/watch?v=MJBjGnR6vlk", 1795 | "translate_status": "translation-not-started" 1796 | } 1797 | } 1798 | ] 1799 | }, 1800 | { 1801 | "id": 2162, 1802 | "index": 5, 1803 | "title": "三角方程等式", 1804 | "en_title": "Trigonometric equations and identities", 1805 | "lectures": [ 1806 | { 1807 | "id": 33081, 1808 | "index": 1, 1809 | "title": null, 1810 | "en_title": "Introduction to arcsine", 1811 | "brief": "", 1812 | "onshow": true, 1813 | "attachment_total": 0, 1814 | "attachments": [], 1815 | "type": "video", 1816 | "resource": { 1817 | "id": 19726, 1818 | "duration_secs": 635, 1819 | "duration_msecs": 635200, 1820 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034225283_4194.jpg", 1821 | "lecture_id": 33081, 1822 | "guid": "c1013c51-9a24-4003-8405-7cde090b3112", 1823 | "brief": "", 1824 | "content": [ 1825 | { 1826 | "name": "360p", 1827 | "url": "http://alicdn.yxgapp.com/miniVideos/c1013c51-9a24-4003-8405-7cde090b3112.mp4", 1828 | "size": 10951947, 1829 | "size_mb": 10.44, 1830 | "priority": 1 1831 | } 1832 | ], 1833 | "comment_number": 0, 1834 | "collect_number": 0, 1835 | "source_url": "http://www.youtube.com/watch?v=JGU74wbZMLg", 1836 | "translate_status": "translation-not-started" 1837 | } 1838 | }, 1839 | { 1840 | "id": 33082, 1841 | "index": 2, 1842 | "title": null, 1843 | "en_title": "Introduction to arctangent", 1844 | "brief": "", 1845 | "onshow": true, 1846 | "attachment_total": 0, 1847 | "attachments": [], 1848 | "type": "video", 1849 | "resource": { 1850 | "id": 19727, 1851 | "duration_secs": 605, 1852 | "duration_msecs": 605933, 1853 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034259899_5392.jpg", 1854 | "lecture_id": 33082, 1855 | "guid": "d1a9cf2b-783c-4c72-b53c-dbb14f8e2d2e", 1856 | "brief": "", 1857 | "content": [ 1858 | { 1859 | "name": "360p", 1860 | "url": "http://alicdn.yxgapp.com/miniVideos/d1a9cf2b-783c-4c72-b53c-dbb14f8e2d2e.mp4", 1861 | "size": 11080324, 1862 | "size_mb": 10.57, 1863 | "priority": 1 1864 | } 1865 | ], 1866 | "comment_number": 0, 1867 | "collect_number": 0, 1868 | "source_url": "http://www.youtube.com/watch?v=Idxeo49szW0", 1869 | "translate_status": "translation-not-started" 1870 | } 1871 | }, 1872 | { 1873 | "id": 33083, 1874 | "index": 3, 1875 | "title": null, 1876 | "en_title": "Introduction to arccosine", 1877 | "brief": "", 1878 | "onshow": true, 1879 | "attachment_total": 0, 1880 | "attachments": [], 1881 | "type": "video", 1882 | "resource": { 1883 | "id": 19728, 1884 | "duration_secs": 817, 1885 | "duration_msecs": 817467, 1886 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034312621_8592.jpg", 1887 | "lecture_id": 33083, 1888 | "guid": "9d3d2d06-f68e-40a9-b980-350bdb4eae97", 1889 | "brief": "", 1890 | "content": [ 1891 | { 1892 | "name": "360p", 1893 | "url": "http://alicdn.yxgapp.com/miniVideos/9d3d2d06-f68e-40a9-b980-350bdb4eae97.mp4", 1894 | "size": 14984281, 1895 | "size_mb": 14.29, 1896 | "priority": 1 1897 | } 1898 | ], 1899 | "comment_number": 0, 1900 | "collect_number": 0, 1901 | "source_url": "http://www.youtube.com/watch?v=eTDaJ4ebK28", 1902 | "translate_status": "translation-not-started" 1903 | } 1904 | }, 1905 | { 1906 | "id": 33084, 1907 | "index": 4, 1908 | "title": null, 1909 | "en_title": "How to restrict the domain of a function to make it invertible (example)", 1910 | "brief": "", 1911 | "onshow": true, 1912 | "attachment_total": 0, 1913 | "attachments": [], 1914 | "type": "video", 1915 | "resource": { 1916 | "id": 19729, 1917 | "duration_secs": 359, 1918 | "duration_msecs": 359533, 1919 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034327572_5704.jpg", 1920 | "lecture_id": 33084, 1921 | "guid": "4a1c32e8-d0d7-4cd8-9977-e2453777cf21", 1922 | "brief": "", 1923 | "content": [ 1924 | { 1925 | "name": "360p", 1926 | "url": "http://alicdn.yxgapp.com/miniVideos/4a1c32e8-d0d7-4cd8-9977-e2453777cf21.mp4", 1927 | "size": 5653962, 1928 | "size_mb": 5.39, 1929 | "priority": 1 1930 | } 1931 | ], 1932 | "comment_number": 0, 1933 | "collect_number": 0, 1934 | "source_url": "http://www.youtube.com/watch?v=tCV9VyIIaw0", 1935 | "translate_status": "translation-not-started" 1936 | } 1937 | }, 1938 | { 1939 | "id": 33085, 1940 | "index": 5, 1941 | "title": null, 1942 | "en_title": "Restricting the domain of a tangent function to make it invertible", 1943 | "brief": "", 1944 | "onshow": true, 1945 | "attachment_total": 0, 1946 | "attachments": [], 1947 | "type": "video", 1948 | "resource": { 1949 | "id": 19730, 1950 | "duration_secs": 583, 1951 | "duration_msecs": 583467, 1952 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034363131_9233.jpg", 1953 | "lecture_id": 33085, 1954 | "guid": "a4f53445-0b22-41f9-92ac-dfa3f15c0d64", 1955 | "brief": "", 1956 | "content": [ 1957 | { 1958 | "name": "360p", 1959 | "url": "http://alicdn.yxgapp.com/miniVideos/a4f53445-0b22-41f9-92ac-dfa3f15c0d64.mp4", 1960 | "size": 8932222, 1961 | "size_mb": 8.52, 1962 | "priority": 1 1963 | } 1964 | ], 1965 | "comment_number": 0, 1966 | "collect_number": 0, 1967 | "source_url": "http://www.youtube.com/watch?v=QGfdhqbilY8", 1968 | "translate_status": "translation-not-started" 1969 | } 1970 | }, 1971 | { 1972 | "id": 33086, 1973 | "index": 6, 1974 | "title": null, 1975 | "en_title": "How to find an angle given its tangent using a calculator (example)", 1976 | "brief": "", 1977 | "onshow": true, 1978 | "attachment_total": 0, 1979 | "attachments": [], 1980 | "type": "video", 1981 | "resource": { 1982 | "id": 19731, 1983 | "duration_secs": 214, 1984 | "duration_msecs": 214800, 1985 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034373853_6139.jpg", 1986 | "lecture_id": 33086, 1987 | "guid": "77504aa1-f377-4227-9472-25d2bd3d7c8c", 1988 | "brief": "", 1989 | "content": [ 1990 | { 1991 | "name": "360p", 1992 | "url": "http://alicdn.yxgapp.com/miniVideos/77504aa1-f377-4227-9472-25d2bd3d7c8c.mp4", 1993 | "size": 3415193, 1994 | "size_mb": 3.26, 1995 | "priority": 1 1996 | } 1997 | ], 1998 | "comment_number": 0, 1999 | "collect_number": 0, 2000 | "source_url": "http://www.youtube.com/watch?v=MABWdzmZFIQ", 2001 | "translate_status": "translation-not-started" 2002 | } 2003 | }, 2004 | { 2005 | "id": 33087, 2006 | "index": 7, 2007 | "title": null, 2008 | "en_title": "How to find all solutions to an equation of the form sin(x)=d (example)", 2009 | "brief": "", 2010 | "onshow": true, 2011 | "attachment_total": 0, 2012 | "attachments": [], 2013 | "type": "video", 2014 | "resource": { 2015 | "id": 19732, 2016 | "duration_secs": 369, 2017 | "duration_msecs": 369067, 2018 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034398779_4827.jpg", 2019 | "lecture_id": 33087, 2020 | "guid": "505ae5db-c1e7-4cec-a758-17503f80931a", 2021 | "brief": "", 2022 | "content": [ 2023 | { 2024 | "name": "360p", 2025 | "url": "http://alicdn.yxgapp.com/miniVideos/505ae5db-c1e7-4cec-a758-17503f80931a.mp4", 2026 | "size": 5710944, 2027 | "size_mb": 5.45, 2028 | "priority": 1 2029 | } 2030 | ], 2031 | "comment_number": 0, 2032 | "collect_number": 0, 2033 | "source_url": "http://www.youtube.com/watch?v=NC7iWEQ9Kug", 2034 | "translate_status": "translation-not-started" 2035 | } 2036 | }, 2037 | { 2038 | "id": 33088, 2039 | "index": 8, 2040 | "title": null, 2041 | "en_title": "Solving cos(θ)=1 and cos(θ)=-1", 2042 | "brief": "", 2043 | "onshow": true, 2044 | "attachment_total": 0, 2045 | "attachments": [], 2046 | "type": "video", 2047 | "resource": { 2048 | "id": 19733, 2049 | "duration_secs": 317, 2050 | "duration_msecs": 317600, 2051 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034415134_4169.jpg", 2052 | "lecture_id": 33088, 2053 | "guid": "14522fee-1fee-484d-9aed-4985ff52bb65", 2054 | "brief": "", 2055 | "content": [ 2056 | { 2057 | "name": "360p", 2058 | "url": "http://alicdn.yxgapp.com/miniVideos/14522fee-1fee-484d-9aed-4985ff52bb65.mp4", 2059 | "size": 4792910, 2060 | "size_mb": 4.57, 2061 | "priority": 1 2062 | } 2063 | ], 2064 | "comment_number": 0, 2065 | "collect_number": 0, 2066 | "source_url": "http://www.youtube.com/watch?v=SdHwokUU8xI", 2067 | "translate_status": "translation-not-started" 2068 | } 2069 | }, 2070 | { 2071 | "id": 33089, 2072 | "index": 9, 2073 | "title": null, 2074 | "en_title": "Word problem: Solving a sinusoidal modeling function", 2075 | "brief": "", 2076 | "onshow": true, 2077 | "attachment_total": 0, 2078 | "attachments": [], 2079 | "type": "video", 2080 | "resource": { 2081 | "id": 19734, 2082 | "duration_secs": 502, 2083 | "duration_msecs": 317600, 2084 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034445446_1618.jpg", 2085 | "lecture_id": 33089, 2086 | "guid": "fcdcde83-0603-4c6b-a986-73b89eb0c2b3", 2087 | "brief": "", 2088 | "content": [ 2089 | { 2090 | "name": "360p", 2091 | "url": "http://alicdn.yxgapp.com/miniVideos/fcdcde83-0603-4c6b-a986-73b89eb0c2b3.mp4", 2092 | "size": 4792910, 2093 | "size_mb": 4.57, 2094 | "priority": 1 2095 | } 2096 | ], 2097 | "comment_number": 0, 2098 | "collect_number": 0, 2099 | "source_url": "http://www.youtube.com/watch?v=2pwnr_soZEU", 2100 | "translate_status": "translation-not-started" 2101 | } 2102 | }, 2103 | { 2104 | "id": 33090, 2105 | "index": 10, 2106 | "title": null, 2107 | "en_title": "Review of the trigonometric angle addition identities", 2108 | "brief": "", 2109 | "onshow": true, 2110 | "attachment_total": 0, 2111 | "attachments": [], 2112 | "type": "video", 2113 | "resource": { 2114 | "id": 19735, 2115 | "duration_secs": 666, 2116 | "duration_msecs": 666133, 2117 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034482335_6829.jpg", 2118 | "lecture_id": 33090, 2119 | "guid": "1eab3a6f-e9ab-410f-a80b-8222b661c8d1", 2120 | "brief": "", 2121 | "content": [ 2122 | { 2123 | "name": "360p", 2124 | "url": "http://alicdn.yxgapp.com/miniVideos/1eab3a6f-e9ab-410f-a80b-8222b661c8d1.mp4", 2125 | "size": 10791725, 2126 | "size_mb": 10.29, 2127 | "priority": 1 2128 | } 2129 | ], 2130 | "comment_number": 0, 2131 | "collect_number": 0, 2132 | "source_url": "http://www.youtube.com/watch?v=a70-dYvDJZY", 2133 | "translate_status": "translation-not-started" 2134 | } 2135 | }, 2136 | { 2137 | "id": 33091, 2138 | "index": 11, 2139 | "title": null, 2140 | "en_title": "How to use the cosine angle addition identity (example)", 2141 | "brief": "", 2142 | "onshow": true, 2143 | "attachment_total": 0, 2144 | "attachments": [], 2145 | "type": "video", 2146 | "resource": { 2147 | "id": 19736, 2148 | "duration_secs": 313, 2149 | "duration_msecs": 666133, 2150 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034495488_6719.jpg", 2151 | "lecture_id": 33091, 2152 | "guid": "f181f50d-49e7-4c4f-9351-accf3fbc387d", 2153 | "brief": "", 2154 | "content": [ 2155 | { 2156 | "name": "360p", 2157 | "url": "http://alicdn.yxgapp.com/miniVideos/f181f50d-49e7-4c4f-9351-accf3fbc387d.mp4", 2158 | "size": 10791725, 2159 | "size_mb": 10.29, 2160 | "priority": 1 2161 | } 2162 | ], 2163 | "comment_number": 0, 2164 | "collect_number": 0, 2165 | "source_url": "http://www.youtube.com/watch?v=h0SNEO25vIw", 2166 | "translate_status": "translation-not-started" 2167 | } 2168 | }, 2169 | { 2170 | "id": 33092, 2171 | "index": 12, 2172 | "title": null, 2173 | "en_title": "How to use the cosine double-angle identity (example)", 2174 | "brief": "", 2175 | "onshow": true, 2176 | "attachment_total": 0, 2177 | "attachments": [], 2178 | "type": "video", 2179 | "resource": { 2180 | "id": 19737, 2181 | "duration_secs": 207, 2182 | "duration_msecs": 207133, 2183 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034506158_6576.jpg", 2184 | "lecture_id": 33092, 2185 | "guid": "99502876-b352-44c8-bd01-fa0c79877890", 2186 | "brief": "", 2187 | "content": [ 2188 | { 2189 | "name": "360p", 2190 | "url": "http://alicdn.yxgapp.com/miniVideos/99502876-b352-44c8-bd01-fa0c79877890.mp4", 2191 | "size": 3027946, 2192 | "size_mb": 2.89, 2193 | "priority": 1 2194 | } 2195 | ], 2196 | "comment_number": 0, 2197 | "collect_number": 1, 2198 | "source_url": "http://www.youtube.com/watch?v=D_smr0GBPvA", 2199 | "translate_status": "translation-not-started" 2200 | } 2201 | }, 2202 | { 2203 | "id": 33093, 2204 | "index": 13, 2205 | "title": null, 2206 | "en_title": "Proof of the sine angle addition identity", 2207 | "brief": "", 2208 | "onshow": true, 2209 | "attachment_total": 0, 2210 | "attachments": [], 2211 | "type": "video", 2212 | "resource": { 2213 | "id": 19738, 2214 | "duration_secs": 505, 2215 | "duration_msecs": 505067, 2216 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034531745_5328.jpg", 2217 | "lecture_id": 33093, 2218 | "guid": "87df715c-986a-4072-bbd5-3e3a90d165c2", 2219 | "brief": "", 2220 | "content": [ 2221 | { 2222 | "name": "360p", 2223 | "url": "http://alicdn.yxgapp.com/miniVideos/87df715c-986a-4072-bbd5-3e3a90d165c2.mp4", 2224 | "size": 7601140, 2225 | "size_mb": 7.25, 2226 | "priority": 1 2227 | } 2228 | ], 2229 | "comment_number": 0, 2230 | "collect_number": 0, 2231 | "source_url": "http://www.youtube.com/watch?v=R0EQg9vgbQw", 2232 | "translate_status": "translation-not-started" 2233 | } 2234 | }, 2235 | { 2236 | "id": 33094, 2237 | "index": 14, 2238 | "title": null, 2239 | "en_title": "Proof of the cosine angle addition identity", 2240 | "brief": "", 2241 | "onshow": true, 2242 | "attachment_total": 0, 2243 | "attachments": [], 2244 | "type": "video", 2245 | "resource": { 2246 | "id": 19739, 2247 | "duration_secs": 361, 2248 | "duration_msecs": 361067, 2249 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034550179_2534.jpg", 2250 | "lecture_id": 33094, 2251 | "guid": "dcecde7b-1cf9-4085-b0e4-76f67f264c09", 2252 | "brief": "", 2253 | "content": [ 2254 | { 2255 | "name": "360p", 2256 | "url": "http://alicdn.yxgapp.com/miniVideos/dcecde7b-1cf9-4085-b0e4-76f67f264c09.mp4", 2257 | "size": 5604875, 2258 | "size_mb": 5.35, 2259 | "priority": 1 2260 | } 2261 | ], 2262 | "comment_number": 0, 2263 | "collect_number": 0, 2264 | "source_url": "http://www.youtube.com/watch?v=0VBQnR2h8XM", 2265 | "translate_status": "translation-not-started" 2266 | } 2267 | }, 2268 | { 2269 | "id": 33095, 2270 | "index": 15, 2271 | "title": null, 2272 | "en_title": "How to find trigonometric values using the angle addition formulas (example)", 2273 | "brief": "", 2274 | "onshow": true, 2275 | "attachment_total": 0, 2276 | "attachments": [], 2277 | "type": "video", 2278 | "resource": { 2279 | "id": 19740, 2280 | "duration_secs": 514, 2281 | "duration_msecs": 361067, 2282 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034576201_9291.jpg", 2283 | "lecture_id": 33095, 2284 | "guid": "f0fad9a4-e832-44f7-bb4a-7ae89358522f", 2285 | "brief": "", 2286 | "content": [ 2287 | { 2288 | "name": "360p", 2289 | "url": "http://alicdn.yxgapp.com/miniVideos/f0fad9a4-e832-44f7-bb4a-7ae89358522f.mp4", 2290 | "size": 5604875, 2291 | "size_mb": 5.35, 2292 | "priority": 1 2293 | } 2294 | ], 2295 | "comment_number": 0, 2296 | "collect_number": 0, 2297 | "source_url": "http://www.youtube.com/watch?v=2RbKfRfzD-M", 2298 | "translate_status": "translation-not-started" 2299 | } 2300 | }, 2301 | { 2302 | "id": 33096, 2303 | "index": 16, 2304 | "title": null, 2305 | "en_title": "How to find a side length using the trig angle addition identities (example)", 2306 | "brief": "", 2307 | "onshow": true, 2308 | "attachment_total": 0, 2309 | "attachments": [], 2310 | "type": "video", 2311 | "resource": { 2312 | "id": 19741, 2313 | "duration_secs": 319, 2314 | "duration_msecs": 319667, 2315 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034591276_5446.jpg", 2316 | "lecture_id": 33096, 2317 | "guid": "399fc1d0-9e11-4650-b925-c57430713025", 2318 | "brief": "", 2319 | "content": [ 2320 | { 2321 | "name": "360p", 2322 | "url": "http://alicdn.yxgapp.com/miniVideos/399fc1d0-9e11-4650-b925-c57430713025.mp4", 2323 | "size": 4725990, 2324 | "size_mb": 4.51, 2325 | "priority": 1 2326 | } 2327 | ], 2328 | "comment_number": 0, 2329 | "collect_number": 0, 2330 | "source_url": "http://www.youtube.com/watch?v=sI789G6FBb4", 2331 | "translate_status": "translation-not-started" 2332 | } 2333 | }, 2334 | { 2335 | "id": 33097, 2336 | "index": 17, 2337 | "title": null, 2338 | "en_title": "How to manipulate expressions using the trig angle addition formulas (example)", 2339 | "brief": "", 2340 | "onshow": true, 2341 | "attachment_total": 0, 2342 | "attachments": [], 2343 | "type": "video", 2344 | "resource": { 2345 | "id": 19742, 2346 | "duration_secs": 371, 2347 | "duration_msecs": 371000, 2348 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034614673_4255.jpg", 2349 | "lecture_id": 33097, 2350 | "guid": "e244d717-ac47-42a6-acb0-b8ee04d6f4c7", 2351 | "brief": "", 2352 | "content": [ 2353 | { 2354 | "name": "360p", 2355 | "url": "http://alicdn.yxgapp.com/miniVideos/e244d717-ac47-42a6-acb0-b8ee04d6f4c7.mp4", 2356 | "size": 5545925, 2357 | "size_mb": 5.29, 2358 | "priority": 1 2359 | } 2360 | ], 2361 | "comment_number": 0, 2362 | "collect_number": 0, 2363 | "source_url": "http://www.youtube.com/watch?v=yV4Xa8Xtmrc", 2364 | "translate_status": "translation-not-started" 2365 | } 2366 | }, 2367 | { 2368 | "id": 33098, 2369 | "index": 18, 2370 | "title": null, 2371 | "en_title": "Rewriting trigonometric expressions using trigonometric identities", 2372 | "brief": "", 2373 | "onshow": true, 2374 | "attachment_total": 0, 2375 | "attachments": [], 2376 | "type": "video", 2377 | "resource": { 2378 | "id": 19743, 2379 | "duration_secs": 186, 2380 | "duration_msecs": 186133, 2381 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034618776_2524.jpg", 2382 | "lecture_id": 33098, 2383 | "guid": "7e0ff24c-971e-4104-b134-525c49959823", 2384 | "brief": "", 2385 | "content": [ 2386 | { 2387 | "name": "360p", 2388 | "url": "http://alicdn.yxgapp.com/miniVideos/7e0ff24c-971e-4104-b134-525c49959823.mp4", 2389 | "size": 2690505, 2390 | "size_mb": 2.57, 2391 | "priority": 1 2392 | } 2393 | ], 2394 | "comment_number": 0, 2395 | "collect_number": 0, 2396 | "source_url": "http://www.youtube.com/watch?v=4OEeVLo5V1o", 2397 | "translate_status": "translation-not-started" 2398 | } 2399 | }, 2400 | { 2401 | "id": 33099, 2402 | "index": 19, 2403 | "title": null, 2404 | "en_title": "Challenge problem: Find the area of a triangle", 2405 | "brief": "", 2406 | "onshow": true, 2407 | "attachment_total": 0, 2408 | "attachments": [], 2409 | "type": "video", 2410 | "resource": { 2411 | "id": 19744, 2412 | "duration_secs": 994, 2413 | "duration_msecs": 994933, 2414 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034677507_4217.jpg", 2415 | "lecture_id": 33099, 2416 | "guid": "b9ff54f0-8110-43e5-ab0c-a82864184e2a", 2417 | "brief": "", 2418 | "content": [ 2419 | { 2420 | "name": "360p", 2421 | "url": "http://alicdn.yxgapp.com/miniVideos/b9ff54f0-8110-43e5-ab0c-a82864184e2a.mp4", 2422 | "size": 16737491, 2423 | "size_mb": 15.96, 2424 | "priority": 1 2425 | } 2426 | ], 2427 | "comment_number": 0, 2428 | "collect_number": 0, 2429 | "source_url": "http://www.youtube.com/watch?v=smtrrefmC40", 2430 | "translate_status": "translation-not-started" 2431 | } 2432 | }, 2433 | { 2434 | "id": 33100, 2435 | "index": 20, 2436 | "title": null, 2437 | "en_title": "Challenge problem: Find the area of a hexagon", 2438 | "brief": "", 2439 | "onshow": true, 2440 | "attachment_total": 0, 2441 | "attachments": [], 2442 | "type": "video", 2443 | "resource": { 2444 | "id": 19745, 2445 | "duration_secs": 1097, 2446 | "duration_msecs": 1097133, 2447 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034735947_5950.jpg", 2448 | "lecture_id": 33100, 2449 | "guid": "1f475391-4ce7-47e0-8094-fd990e998162", 2450 | "brief": "", 2451 | "content": [ 2452 | { 2453 | "name": "360p", 2454 | "url": "http://alicdn.yxgapp.com/miniVideos/1f475391-4ce7-47e0-8094-fd990e998162.mp4", 2455 | "size": 17630442, 2456 | "size_mb": 16.81, 2457 | "priority": 1 2458 | } 2459 | ], 2460 | "comment_number": 0, 2461 | "collect_number": 0, 2462 | "source_url": "http://www.youtube.com/watch?v=Ec-BKdC8vOo", 2463 | "translate_status": "translation-not-started" 2464 | } 2465 | }, 2466 | { 2467 | "id": 33101, 2468 | "index": 21, 2469 | "title": null, 2470 | "en_title": "Challenge problem: Find cosine of sum of angles given their cosines", 2471 | "brief": "", 2472 | "onshow": true, 2473 | "attachment_total": 0, 2474 | "attachments": [], 2475 | "type": "video", 2476 | "resource": { 2477 | "id": 19746, 2478 | "duration_secs": 433, 2479 | "duration_msecs": 433600, 2480 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034762893_9969.jpg", 2481 | "lecture_id": 33101, 2482 | "guid": "a26cafe8-7e25-40a7-a4ee-ee4bb407a517", 2483 | "brief": "", 2484 | "content": [ 2485 | { 2486 | "name": "360p", 2487 | "url": "http://alicdn.yxgapp.com/miniVideos/a26cafe8-7e25-40a7-a4ee-ee4bb407a517.mp4", 2488 | "size": 6488865, 2489 | "size_mb": 6.19, 2490 | "priority": 1 2491 | } 2492 | ], 2493 | "comment_number": 0, 2494 | "collect_number": 0, 2495 | "source_url": "http://www.youtube.com/watch?v=XGpEHj43kcc", 2496 | "translate_status": "translation-not-started" 2497 | } 2498 | }, 2499 | { 2500 | "id": 33102, 2501 | "index": 22, 2502 | "title": null, 2503 | "en_title": "Challenge problem: Rewrite a trigonometric equation of angles in arithmetic progression", 2504 | "brief": "", 2505 | "onshow": true, 2506 | "attachment_total": 0, 2507 | "attachments": [], 2508 | "type": "video", 2509 | "resource": { 2510 | "id": 19747, 2511 | "duration_secs": 912, 2512 | "duration_msecs": 912933, 2513 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034810324_5661.jpg", 2514 | "lecture_id": 33102, 2515 | "guid": "777b2c85-4f99-4e73-a0d3-92104fd61800", 2516 | "brief": "", 2517 | "content": [ 2518 | { 2519 | "name": "360p", 2520 | "url": "http://alicdn.yxgapp.com/miniVideos/777b2c85-4f99-4e73-a0d3-92104fd61800.mp4", 2521 | "size": 14296191, 2522 | "size_mb": 13.63, 2523 | "priority": 1 2524 | } 2525 | ], 2526 | "comment_number": 0, 2527 | "collect_number": 0, 2528 | "source_url": "http://www.youtube.com/watch?v=X7GT9JKoAbo", 2529 | "translate_status": "translation-not-started" 2530 | } 2531 | }, 2532 | { 2533 | "id": 33103, 2534 | "index": 23, 2535 | "title": null, 2536 | "en_title": "Challenge problem: Find the maximum value of a trigonometric expression", 2537 | "brief": "", 2538 | "onshow": true, 2539 | "attachment_total": 0, 2540 | "attachments": [], 2541 | "type": "video", 2542 | "resource": { 2543 | "id": 19748, 2544 | "duration_secs": 593, 2545 | "duration_msecs": 593800, 2546 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034840146_8554.jpg", 2547 | "lecture_id": 33103, 2548 | "guid": "2d047f5c-fa89-44bd-a5cc-09c934b08e05", 2549 | "brief": "", 2550 | "content": [ 2551 | { 2552 | "name": "360p", 2553 | "url": "http://alicdn.yxgapp.com/miniVideos/2d047f5c-fa89-44bd-a5cc-09c934b08e05.mp4", 2554 | "size": 9227107, 2555 | "size_mb": 8.8, 2556 | "priority": 1 2557 | } 2558 | ], 2559 | "comment_number": 0, 2560 | "collect_number": 1, 2561 | "source_url": "http://www.youtube.com/watch?v=tzR9jUCSniQ", 2562 | "translate_status": "translation-not-started" 2563 | } 2564 | }, 2565 | { 2566 | "id": 33104, 2567 | "index": 24, 2568 | "title": null, 2569 | "en_title": "Challenge problem: System of trigonometric equations", 2570 | "brief": "", 2571 | "onshow": true, 2572 | "attachment_total": 0, 2573 | "attachments": [], 2574 | "type": "video", 2575 | "resource": { 2576 | "id": 19749, 2577 | "duration_secs": 768, 2578 | "duration_msecs": 768267, 2579 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034880057_300.jpg", 2580 | "lecture_id": 33104, 2581 | "guid": "04108e90-3b53-4e7d-a4db-191e1e38881b", 2582 | "brief": "", 2583 | "content": [ 2584 | { 2585 | "name": "360p", 2586 | "url": "http://alicdn.yxgapp.com/miniVideos/04108e90-3b53-4e7d-a4db-191e1e38881b.mp4", 2587 | "size": 12121404, 2588 | "size_mb": 11.56, 2589 | "priority": 1 2590 | } 2591 | ], 2592 | "comment_number": 0, 2593 | "collect_number": 0, 2594 | "source_url": "http://www.youtube.com/watch?v=EjtjdJZ2x8w", 2595 | "translate_status": "translation-not-started" 2596 | } 2597 | }, 2598 | { 2599 | "id": 33105, 2600 | "index": 25, 2601 | "title": null, 2602 | "en_title": "Challenge problem: Conditions on angle for a system of equation having a solution", 2603 | "brief": "", 2604 | "onshow": true, 2605 | "attachment_total": 0, 2606 | "attachments": [], 2607 | "type": "video", 2608 | "resource": { 2609 | "id": 19750, 2610 | "duration_secs": 662, 2611 | "duration_msecs": 661933, 2612 | "cover_url": "https://gcdp.oss-cn-qingdao.aliyuncs.com/201603/3/1457034916535_1886.jpg", 2613 | "lecture_id": 33105, 2614 | "guid": "054f70f7-5534-4c0e-a8ec-729e232915e6", 2615 | "brief": "", 2616 | "content": [ 2617 | { 2618 | "name": "360p", 2619 | "url": "http://alicdn.yxgapp.com/miniVideos/054f70f7-5534-4c0e-a8ec-729e232915e6.mp4", 2620 | "size": 10367389, 2621 | "size_mb": 9.89, 2622 | "priority": 1 2623 | } 2624 | ], 2625 | "comment_number": 0, 2626 | "collect_number": 0, 2627 | "source_url": "http://www.youtube.com/watch?v=Y2ed-g8Lpdc", 2628 | "translate_status": "translation-not-started" 2629 | } 2630 | } 2631 | ] 2632 | } 2633 | ], 2634 | "latest_learning_process": null 2635 | } 2636 | } 2637 | -------------------------------------------------------------------------------- /prisma/migrations/20221004064203_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Video" ( 3 | "id" SERIAL NOT NULL, 4 | "title" TEXT NOT NULL, 5 | "desc" TEXT, 6 | "pic" TEXT NOT NULL, 7 | "authorId" INTEGER NOT NULL, 8 | "categoryId" INTEGER NOT NULL, 9 | "level" INTEGER NOT NULL DEFAULT 1, 10 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | "updatedAt" TIMESTAMP(3) NOT NULL, 12 | 13 | CONSTRAINT "Video_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- CreateTable 17 | CREATE TABLE "Category" ( 18 | "id" SERIAL NOT NULL, 19 | "name" TEXT NOT NULL, 20 | 21 | CONSTRAINT "Category_pkey" PRIMARY KEY ("id") 22 | ); 23 | 24 | -- CreateTable 25 | CREATE TABLE "Chapter" ( 26 | "id" SERIAL NOT NULL, 27 | "title" TEXT NOT NULL, 28 | "url" TEXT NOT NULL, 29 | "videoId" INTEGER NOT NULL, 30 | 31 | CONSTRAINT "Chapter_pkey" PRIMARY KEY ("id") 32 | ); 33 | 34 | -- CreateTable 35 | CREATE TABLE "User" ( 36 | "id" SERIAL NOT NULL, 37 | "avatar" TEXT NOT NULL, 38 | "name" TEXT NOT NULL, 39 | 40 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 41 | ); 42 | 43 | -- CreateIndex 44 | CREATE UNIQUE INDEX "Video_title_key" ON "Video"("title"); 45 | 46 | -- CreateIndex 47 | CREATE UNIQUE INDEX "Category_name_key" ON "Category"("name"); 48 | 49 | -- AddForeignKey 50 | ALTER TABLE "Video" ADD CONSTRAINT "Video_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 51 | 52 | -- AddForeignKey 53 | ALTER TABLE "Video" ADD CONSTRAINT "Video_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 54 | 55 | -- AddForeignKey 56 | ALTER TABLE "Chapter" ADD CONSTRAINT "Chapter_videoId_fkey" FOREIGN KEY ("videoId") REFERENCES "Video"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 57 | -------------------------------------------------------------------------------- /prisma/migrations/20221004072200_add_cover/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `cover` to the `Chapter` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Chapter" ADD COLUMN "cover" TEXT NOT NULL; 9 | -------------------------------------------------------------------------------- /prisma/migrations/20221012072321_user/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint. 5 | - You are about to drop the column `avatar` on the `User` table. All the data in the column will be lost. 6 | - A unique constraint covering the columns `[email]` on the table `User` will be added. If there are existing duplicate values, this will fail. 7 | 8 | */ 9 | -- DropForeignKey 10 | ALTER TABLE "Video" DROP CONSTRAINT "Video_authorId_fkey"; 11 | 12 | -- AlterTable 13 | ALTER TABLE "User" DROP CONSTRAINT "User_pkey", 14 | DROP COLUMN "avatar", 15 | ADD COLUMN "email" TEXT, 16 | ADD COLUMN "emailVerified" TIMESTAMP(3), 17 | ADD COLUMN "image" TEXT, 18 | ALTER COLUMN "id" DROP DEFAULT, 19 | ALTER COLUMN "id" SET DATA TYPE TEXT, 20 | ALTER COLUMN "name" DROP NOT NULL, 21 | ADD CONSTRAINT "User_pkey" PRIMARY KEY ("id"); 22 | DROP SEQUENCE "User_id_seq"; 23 | 24 | -- AlterTable 25 | ALTER TABLE "Video" ALTER COLUMN "authorId" SET DATA TYPE TEXT; 26 | 27 | -- CreateTable 28 | CREATE TABLE "Account" ( 29 | "id" TEXT NOT NULL, 30 | "userId" TEXT NOT NULL, 31 | "type" TEXT NOT NULL, 32 | "provider" TEXT NOT NULL, 33 | "providerAccountId" TEXT NOT NULL, 34 | "refresh_token" TEXT, 35 | "access_token" TEXT, 36 | "expires_at" INTEGER, 37 | "token_type" TEXT, 38 | "scope" TEXT, 39 | "id_token" TEXT, 40 | "session_state" TEXT, 41 | 42 | CONSTRAINT "Account_pkey" PRIMARY KEY ("id") 43 | ); 44 | 45 | -- CreateTable 46 | CREATE TABLE "Session" ( 47 | "id" TEXT NOT NULL, 48 | "sessionToken" TEXT NOT NULL, 49 | "userId" TEXT NOT NULL, 50 | "expires" TIMESTAMP(3) NOT NULL, 51 | 52 | CONSTRAINT "Session_pkey" PRIMARY KEY ("id") 53 | ); 54 | 55 | -- CreateTable 56 | CREATE TABLE "VerificationToken" ( 57 | "identifier" TEXT NOT NULL, 58 | "token" TEXT NOT NULL, 59 | "expires" TIMESTAMP(3) NOT NULL 60 | ); 61 | 62 | -- CreateIndex 63 | CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); 64 | 65 | -- CreateIndex 66 | CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); 67 | 68 | -- CreateIndex 69 | CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); 70 | 71 | -- CreateIndex 72 | CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token"); 73 | 74 | -- CreateIndex 75 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 76 | 77 | -- AddForeignKey 78 | ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 79 | 80 | -- AddForeignKey 81 | ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 82 | 83 | -- AddForeignKey 84 | ALTER TABLE "Video" ADD CONSTRAINT "Video_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 85 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "postgresql" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model Account { 14 | id String @id @default(cuid()) 15 | userId String 16 | type String 17 | provider String 18 | providerAccountId String 19 | refresh_token String? @db.Text 20 | access_token String? @db.Text 21 | expires_at Int? 22 | token_type String? 23 | scope String? 24 | id_token String? @db.Text 25 | session_state String? 26 | 27 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 28 | 29 | @@unique([provider, providerAccountId]) 30 | } 31 | 32 | model Session { 33 | id String @id @default(cuid()) 34 | sessionToken String @unique 35 | userId String 36 | expires DateTime 37 | user User @relation(fields: [userId], references: [id], onDelete: Cascade) 38 | } 39 | 40 | model User { 41 | id String @id @default(cuid()) 42 | name String? 43 | email String? @unique 44 | emailVerified DateTime? 45 | image String? 46 | accounts Account[] 47 | sessions Session[] 48 | Video Video[] 49 | } 50 | 51 | model VerificationToken { 52 | identifier String 53 | token String @unique 54 | expires DateTime 55 | 56 | @@unique([identifier, token]) 57 | } 58 | 59 | model Video { 60 | id Int @id @default(autoincrement()) 61 | title String @unique 62 | desc String? 63 | pic String 64 | authorId String 65 | author User @relation(fields: [authorId], references: [id]) 66 | categoryId Int 67 | category Category @relation(fields: [categoryId], references: [id]) 68 | level Int @default(1) 69 | createdAt DateTime @default(now()) 70 | updatedAt DateTime @updatedAt 71 | chapter Chapter[] 72 | } 73 | 74 | model Category { 75 | id Int @id @default(autoincrement()) 76 | name String @unique 77 | video Video[] 78 | } 79 | 80 | model Chapter { 81 | id Int @id @default(autoincrement()) 82 | title String 83 | cover String 84 | url String 85 | videoId Int 86 | video Video? @relation(fields: [videoId], references: [id]) 87 | } 88 | -------------------------------------------------------------------------------- /prisma/seed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | import example from "./example2.json"; 3 | 4 | // initialize Prisma Client 5 | const prisma = new PrismaClient(); 6 | 7 | async function main() { 8 | // const { id: categoryId } = await prisma.category.create({ 9 | // data: { 10 | // name: "设计", 11 | // }, 12 | // }); 13 | 14 | const chapters = example.data.outlines.reduce((res, item) => { 15 | item.lectures.forEach((lecture) => { 16 | res.push({ 17 | title: lecture.title ?? lecture.en_title ?? "", 18 | cover: lecture.resource.cover_url, 19 | url: lecture.resource.content[0].url, 20 | }); 21 | }); 22 | 23 | return res; 24 | }, []); 25 | 26 | console.log(chapters); 27 | 28 | await prisma.video.create({ 29 | data: { 30 | title: example.data.title, 31 | desc: example.data.brief, 32 | pic: example.data.cover_url, 33 | categoryId: 1, 34 | authorId: "cl95b4ny10000fjnuhm7rvys5", 35 | chapter: { 36 | createMany: { 37 | data: chapters, 38 | }, 39 | }, 40 | }, 41 | }); 42 | } 43 | 44 | main() 45 | .catch((e) => { 46 | console.error(e); 47 | process.exit(1); 48 | }) 49 | .finally(async () => { 50 | // close Prisma Client at the end 51 | await prisma.$disconnect(); 52 | }); 53 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maqi1520/next-prisma-video-app/be4d3fd885c59eab0fcb624386754b0e3d55156b/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | 118 | @media (prefers-color-scheme: dark) { 119 | .card, 120 | .footer { 121 | border-color: #222; 122 | } 123 | .code { 124 | background: #111; 125 | } 126 | .logo img { 127 | filter: invert(1); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .button { 6 | @apply w-full rounded px-3 py-3 border-0; 7 | } 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./pages/**/*.tsx", "./components/**/*.tsx"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ts-node": { 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | }, 7 | "compilerOptions": { 8 | "target": "es5", 9 | "lib": ["dom", "dom.iterable", "esnext"], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "strict": false, 13 | "forceConsistentCasingInFileNames": true, 14 | "noEmit": true, 15 | "incremental": true, 16 | "esModuleInterop": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "jsx": "preserve", 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /types/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import NextAuth, { DefaultSession } from "next-auth"; 2 | 3 | declare module "next-auth" { 4 | interface Session { 5 | user: { 6 | id: string; 7 | } & DefaultSession["user"]; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vitest-setup.ts: -------------------------------------------------------------------------------- 1 | import { vi, expect, afterEach } from "vitest"; 2 | import { cleanup } from "@testing-library/react"; 3 | import matchers, { 4 | TestingLibraryMatchers, 5 | } from "@testing-library/jest-dom/matchers"; 6 | 7 | declare global { 8 | namespace Vi { 9 | interface JestAssertion 10 | extends jest.Matchers, 11 | TestingLibraryMatchers {} 12 | } 13 | } 14 | 15 | expect.extend(matchers); 16 | 17 | afterEach(() => { 18 | cleanup(); 19 | }); 20 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | test: { 9 | environment: "jsdom", 10 | setupFiles: "./vitest-setup.ts", 11 | }, 12 | resolve: { 13 | alias: { 14 | "@": path.resolve(__dirname, "."), 15 | }, 16 | }, 17 | }); 18 | --------------------------------------------------------------------------------