├── .gitignore ├── README.md ├── jest.config.js ├── package.json ├── src ├── helpers │ ├── getDownload.ts │ └── getInfo.ts ├── index.ts ├── page │ ├── favicon.ico │ ├── home.html │ ├── script.js │ └── style.css └── routes │ ├── apis │ ├── getDownload.ts │ ├── getInfo.ts │ └── index.ts │ ├── home.ts │ └── index.ts ├── tests ├── getInfo.test.ts └── manifest.ts ├── tsconfig.json ├── wrangler.toml └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | sandbox 3 | 4 | # Cloudflare Workers 5 | worker 6 | 7 | # Bun 8 | bun.lockb 9 | 10 | # Logs 11 | logs 12 | *.log 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | .pnpm-debug.log* 18 | 19 | # Diagnostic reports (https://nodejs.org/api/report.html) 20 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 21 | 22 | # Runtime data 23 | pids 24 | *.pid 25 | *.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | *.lcov 34 | 35 | # nyc test coverage 36 | .nyc_output 37 | 38 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 39 | .grunt 40 | 41 | # Bower dependency directory (https://bower.io/) 42 | bower_components 43 | 44 | # node-waf configuration 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | build/Release 49 | 50 | # Dependency directories 51 | node_modules/ 52 | jspm_packages/ 53 | 54 | # Snowpack dependency directory (https://snowpack.dev/) 55 | web_modules/ 56 | 57 | # TypeScript cache 58 | *.tsbuildinfo 59 | 60 | # Optional npm cache directory 61 | .npm 62 | 63 | # Optional eslint cache 64 | .eslintcache 65 | 66 | # Optional stylelint cache 67 | .stylelintcache 68 | 69 | # Microbundle cache 70 | .rpt2_cache/ 71 | .rts2_cache_cjs/ 72 | .rts2_cache_es/ 73 | .rts2_cache_umd/ 74 | 75 | # Optional REPL history 76 | .node_repl_history 77 | 78 | # Output of 'npm pack' 79 | *.tgz 80 | 81 | # Yarn Integrity file 82 | .yarn-integrity 83 | 84 | # dotenv environment variable files 85 | .env 86 | .env.development.local 87 | .env.test.local 88 | .env.production.local 89 | .env.local 90 | 91 | # parcel-bundler cache (https://parceljs.org/) 92 | .cache 93 | .parcel-cache 94 | 95 | # Next.js build output 96 | .next 97 | out 98 | 99 | # Nuxt.js build / generate output 100 | .nuxt 101 | 102 | # Gatsby files 103 | .cache/ 104 | # Comment in the public line in if your project uses Gatsby and not Next.js 105 | # https://nextjs.org/blog/next-9-1#public-directory-support 106 | # public 107 | 108 | # vuepress build output 109 | .vuepress/dist 110 | 111 | # vuepress v2.x temp and cache directory 112 | .temp 113 | .cache 114 | 115 | # Serverless directories 116 | .serverless/ 117 | 118 | # FuseBox cache 119 | .fusebox/ 120 | 121 | # DynamoDB Local files 122 | .dynamodb/ 123 | 124 | # TernJS port file 125 | .tern-port 126 | 127 | # Stores VSCode versions used for testing VSCode extensions 128 | .vscode-test 129 | 130 | # yarn v2 131 | .yarn/cache 132 | .yarn/unplugged 133 | .yarn/build-state.yml 134 | .yarn/install-state.gz 135 | .pnp.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | npm install 3 | npm run dev 4 | ``` 5 | 6 | ``` 7 | npm run deploy 8 | ``` 9 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: "miniflare", 3 | transform: { 4 | "^.+\\.(ts|tsx)$": "esbuild-jest", 5 | }, 6 | moduleNameMapper: { 7 | __STATIC_CONTENT_MANIFEST: "/tests/manifest.ts", 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "wrangler dev src/index.ts", 4 | "deploy": "wrangler deploy --minify src/index.ts", 5 | "test": "jest" 6 | }, 7 | "dependencies": { 8 | "@types/jest": "^29.5.3", 9 | "esbuild": "^0.18.15", 10 | "esbuild-jest": "^0.5.0", 11 | "hono": "^3.2.7", 12 | "jest": "^29.6.1", 13 | "jest-environment-miniflare": "^2.14.0" 14 | }, 15 | "devDependencies": { 16 | "@cloudflare/workers-types": "^4.20230518.0", 17 | "wrangler": "^3.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/helpers/getDownload.ts: -------------------------------------------------------------------------------- 1 | const getDLink = async ({ shareid, uk, sign, timestamp, fs_id }: any, { cookie, jsToken, dpLogid }: any) => { 2 | const queryString = new URLSearchParams({ 3 | app_id: "250528", 4 | web: "1", 5 | channel: "dubox", 6 | clienttype: "0", 7 | jsToken: jsToken, 8 | "dp-logid": dpLogid, 9 | shareid, 10 | uk, 11 | sign, 12 | timestamp, 13 | primaryid: shareid, 14 | product: "share", 15 | nozip: "0", 16 | fid_list: `[${fs_id}]`, 17 | }).toString() 18 | 19 | const response: any = await fetch("https://www.terabox.com/share/download?" + queryString, { 20 | method: "GET", 21 | headers: { 22 | Cookie: cookie, 23 | }, 24 | }).then(async (res) => await res.json()) 25 | 26 | if (response.errno != 0) throw new Error(`Failed get url download | Errno: ${response.errno}`) 27 | 28 | return { 29 | ok: true, 30 | dlink: response.dlink, 31 | } 32 | } 33 | 34 | const getUrlDownload = async (dlink: string, { userAgent, cookie }: any) => { 35 | const response: any = await fetch(dlink, { 36 | redirect: "follow", 37 | // follow: 0, 38 | headers: { 39 | "User-Agent": userAgent, 40 | "Accept-Language": "en-US,en;q=0.5", 41 | "Sec-Fetch-Dest": "empty", 42 | "Sec-Fetch-Mode": "cors", 43 | "Sec-Fetch-Site": "same-origin", 44 | Cookie: cookie, 45 | }, 46 | }) 47 | 48 | if (!response.redirected) throw new Error("Failed get url download") 49 | 50 | return { 51 | ok: true, 52 | downloadLink: response.url, 53 | } 54 | } 55 | 56 | const getDownloadLink = async ( 57 | { shareid, uk, sign, timestamp, fs_id }: any, 58 | { userAgent, cookie, jsToken, dpLogid }: any 59 | ) => { 60 | try { 61 | const { dlink } = await getDLink({ shareid, uk, sign, timestamp, fs_id }, { cookie, jsToken, dpLogid }) 62 | return await getUrlDownload(dlink, { userAgent, cookie }) 63 | } catch (error: any) { 64 | return { 65 | ok: false, 66 | message: error.message, 67 | } 68 | } 69 | } 70 | 71 | export default getDownloadLink 72 | -------------------------------------------------------------------------------- /src/helpers/getInfo.ts: -------------------------------------------------------------------------------- 1 | const getInfoRecursive = async (shortUrl: string, dir: string = "", root: string = "0", cookie: string = "") => { 2 | const queryString = new URLSearchParams({ app_id: "250528", shorturl: shortUrl.slice(1), root, dir }).toString() 3 | 4 | const response: any = await fetch("https://www.terabox.com/share/list?" + queryString, { 5 | method: "GET", 6 | headers: { Cookie: cookie }, 7 | }).then(async (res) => await res.json()) 8 | 9 | if (response.errno != 0) throw new Error("Failed get data") 10 | 11 | const childrenPromises = response.list.map(async (file: any) => ({ 12 | category: file.category, 13 | fs_id: file.fs_id, 14 | is_dir: file.isdir, 15 | size: file.size, 16 | filename: file.server_filename, 17 | create_time: file.server_ctime, 18 | children: file.isdir ? await getInfoRecursive(shortUrl, file.path, "0", cookie) : undefined, 19 | })) 20 | 21 | const children = await Promise.all(childrenPromises) 22 | return children 23 | } 24 | 25 | const getAllInfo = async (shortUrl: string, pwd: string = "") => { 26 | try { 27 | // get Cookie if share link need password 28 | let cookie: string = "" 29 | if (pwd) cookie = await getCookieWithPass(shortUrl, pwd) 30 | 31 | const queryString = new URLSearchParams({ app_id: "250528", shorturl: shortUrl, root: "1" }).toString() 32 | 33 | const response: any = await fetch("https://www.terabox.com/api/shorturlinfo?" + queryString, { 34 | method: "GET", 35 | headers: { Cookie: cookie }, 36 | }).then(async (res) => await res.json()) 37 | 38 | if (response.errno != 0) throw new Error("Failed get data") 39 | 40 | const listPromises = response.list.map(async (file: any) => ({ 41 | category: file.category, 42 | fs_id: file.fs_id, 43 | is_dir: file.isdir, 44 | size: file.size, 45 | filename: file.server_filename, 46 | create_time: file.server_ctime, 47 | children: file.isdir ? await getInfoRecursive(shortUrl, file.path, "0", cookie) : undefined, 48 | })) 49 | 50 | const list = await Promise.all(listPromises) 51 | 52 | return { 53 | ok: true, 54 | shareid: response.shareid, 55 | uk: response.uk, 56 | sign: response.sign, 57 | timestamp: response.timestamp, 58 | list: list, 59 | } 60 | } catch (error: any) { 61 | return { 62 | ok: false, 63 | message: error.message, 64 | } 65 | } 66 | } 67 | 68 | const getCookieWithPass = async (shortUrl: string, pwd: string) => { 69 | const queryString = new URLSearchParams({ app_id: "250528", surl: shortUrl.slice(1) }).toString() 70 | 71 | return await fetch("https://www.terabox.com/share/verify?" + queryString, { 72 | method: "POST", 73 | body: new URLSearchParams({ pwd }), 74 | }).then(async (res) => { 75 | const response: any = await res.json() 76 | if (response.errno != 0) throw new Error("Password wrong!!!") 77 | return res.headers.get("Set-Cookie")?.split(" ")[0] || "" 78 | }) 79 | } 80 | 81 | export default getAllInfo 82 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono" 2 | import routes from "./routes" 3 | 4 | const app = new Hono() 5 | 6 | app.route("/", routes) 7 | 8 | export default app 9 | -------------------------------------------------------------------------------- /src/page/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maiquocthinh/Terabox-DL/3f989e9537904167497699ded45347f33102d798/src/page/favicon.ico -------------------------------------------------------------------------------- /src/page/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | Get Link 16 | 17 | 18 |
19 |

Get Link Terabox

20 |
21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 | 39 | 40 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/page/script.js: -------------------------------------------------------------------------------- 1 | const TreeView = (function () { 2 | function render(data, rootList) { 3 | for (const item of data) { 4 | if (item.isDir) { 5 | // create
  • 6 | const li = document.createElement("li") 7 | li.className = "folder" 8 | li.setAttribute("opened", "true") 9 | 10 | // create
    11 | const folderHeaderDiv = document.createElement("div") 12 | folderHeaderDiv.className = "folder-header" 13 | folderHeaderDiv.innerHTML = ` 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ${item.name}` 22 | 23 | // append
      to
    • 24 | li.appendChild(folderHeaderDiv) 25 | 26 | // If the folder has children, recursively render them 27 | if (item.children && item.children.length > 0) { 28 | // create
        29 | const childrenUl = document.createElement("ul") 30 | childrenUl.className = "children" 31 | // recursively render 32 | render(item.children, childrenUl) 33 | // append
          to
        • 34 | li.appendChild(childrenUl) 35 | } 36 | 37 | // append to rootList 38 | rootList.appendChild(li) 39 | } else { 40 | // create
        • 41 | const li = document.createElement("li") 42 | li.className = "file" 43 | 44 | li.innerHTML = ` 45 | ${item.name}` 46 | 47 | // create