forked from anime-vsub/app
-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Tailwind CSS custom data and simplify VSCode settings - Added `.vscode/tailwind.json` with Tailwind CSS directives and descriptions. - Simplified `.vscode/settings.json` by removing unnecessary configurations and retaining essential settings. * Add offline video download and management functionality - Implement video download to MP4 using HLS streams - Add IDB-keyval for storing video metadata and offline registry - Create UI components for downloading and removing videos - Integrate progress tracking and notifications for download status - Add support for selecting video quality before download - Implement functions to manage offline video data and storage - Update package.json with necessary dependencies for video processing - Optimize Vite configuration for FFmpeg dependencies - Add constants for offline video storage prefixes and registry - Implement utility functions for handling before unload events and listing offline seasons * Add offline mode support with file saving options - Introduced `questionSaveToFile` function to prompt user for file saving location. - Enhanced `download` function to handle saving videos to device or app storage. - Added `getURL` and `getFile` functions for retrieving stored files. - Updated `SeasonOffline` and `ChapsOffline` interfaces to include offline metadata. - Implemented retry logic for fetching poster and image buffers. - Modified `download-to-mp4` worker to support saving video buffers directly to device. - Created `get-vdm-store` utility for caching VDM store instance. * Refactor offline video download logic and improve localization - Removed redundant offline video management functions and constants. - Simplified the `download-to-mp4` worker by eliminating unnecessary parameters. - Updated `vdm.ts` store to streamline download process and remove unused functions. - Enhanced localization support for download-related messages. - Adjusted UI components to reflect changes in download logic. - Updated VSCode settings for i18n-ally integration. * Update VSCode settings for improved development experience - Enable bracket pair colorization and guides - Set Prettier as the default formatter and enable format on save - Configure ESLint validation for multiple languages - Add custom spell check words and enable Ionic preview in editor - Update i18n-ally settings for locale paths and extraction rules - Ignore specific i18n keys in various Vue components - Add TypeScript SDK path and SCSS linting rule - Enable tab completion and code lens in diff editor - Set Bun runtime path for Gitpod environment * remove loss file
- Loading branch information
1 parent
a11188f
commit e78ff97
Showing
16 changed files
with
654 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
{ | ||
"version": 1.1, | ||
"atDirectives": [ | ||
{ | ||
"name": "@tailwind", | ||
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.", | ||
"references": [ | ||
{ | ||
"name": "Tailwind Documentation", | ||
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "@apply", | ||
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.", | ||
"references": [ | ||
{ | ||
"name": "Tailwind Documentation", | ||
"url": "https://tailwindcss.com/docs/functions-and-directives#apply" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "@responsive", | ||
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n", | ||
"references": [ | ||
{ | ||
"name": "Tailwind Documentation", | ||
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "@screen", | ||
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n", | ||
"references": [ | ||
{ | ||
"name": "Tailwind Documentation", | ||
"url": "https://tailwindcss.com/docs/functions-and-directives#screen" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "@variants", | ||
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n", | ||
"references": [ | ||
{ | ||
"name": "Tailwind Documentation", | ||
"url": "https://tailwindcss.com/docs/functions-and-directives#variants" | ||
} | ||
] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import wasmURL from "@ffmpeg/core/wasm?url" | ||
import coreURL from "@ffmpeg/core?url" | ||
import { FFmpeg } from "@ffmpeg/ffmpeg" | ||
import { parse, stringify } from "hls-parser" | ||
import pLimit from "p-limit" | ||
import sha256 from "sha256" | ||
import type { RetryOptions } from "ts-retry"; | ||
import { retryAsync } from "ts-retry" | ||
|
||
/** | ||
* Converts an HLS (HTTP Live Streaming) manifest to an MP4 file. | ||
* | ||
* @param {string} m3u8Content - The content of the HLS manifest file. | ||
* @param {(url: string) => Promise<Uint8Array>} fetchFile - A function to fetch a file from a URL. | ||
* @param {(current: number, total: number, timeCurrent: number, timeDuration: number) => void} onProgress - A callback function to report the progress of the conversion. | ||
* @param {number} [concurrency=20] - The number of concurrent downloads. | ||
* @param {RetryOptions} [retryOptions] - Options for retrying failed downloads. | ||
* @return {Promise<FileData>} The MP4 file data. | ||
*/ | ||
export async function convertHlsToMP4( | ||
m3u8Content: string, | ||
fetchFile: (url: string) => Promise<Uint8Array>, | ||
onProgress: ( | ||
current: number, | ||
total: number, | ||
timeCurrent: number, | ||
timeDuration: number, | ||
) => void, | ||
concurrency = 20, | ||
retryOptions?: RetryOptions, | ||
) { | ||
const ffmpeg = new FFmpeg() | ||
|
||
const hash = sha256(m3u8Content) | ||
const manifest = parse(m3u8Content) | ||
|
||
if (!("segments" in manifest)) | ||
throw new Error("Can't support master playlist") | ||
|
||
if (import.meta.env.DEV) | ||
ffmpeg.on("log", ({ message }) => { | ||
console.log(`[@ffmpeg/ffmpeg]: ${message}`) | ||
}) | ||
|
||
if (import.meta.env.DEV) | ||
ffmpeg.on("progress", ({ progress, time }) => { | ||
console.log(`${progress * 100} % (transcoded time: ${time / 1000000} s)`) | ||
}) | ||
|
||
await ffmpeg.load({ | ||
coreURL, | ||
wasmURL, | ||
}) | ||
|
||
const limit = pLimit(concurrency) | ||
|
||
const timeDuration = manifest.segments.reduce( | ||
(prev, cur) => cur.duration + prev, | ||
0, | ||
) | ||
// Download all the TS segments | ||
let downloaded = 0 | ||
let timeCurrent = 0 | ||
await Promise.all( | ||
manifest.segments.map((segment, i) => | ||
limit(() => | ||
retryAsync<void>(async () => { | ||
const path = `${hash}-${i}.ts` | ||
await ffmpeg.writeFile(path, await fetchFile(segment.uri)) | ||
segment.uri = path | ||
downloaded++ | ||
timeCurrent += segment.duration | ||
onProgress( | ||
downloaded, | ||
manifest.segments.length, | ||
timeCurrent, | ||
timeDuration, | ||
) | ||
}, retryOptions), | ||
), | ||
), | ||
) | ||
await ffmpeg.writeFile(`${hash}-media.m3u8`, stringify(manifest)) | ||
|
||
await ffmpeg.exec([ | ||
"-i", | ||
`${hash}-media.m3u8`, | ||
..."-acodec copy -vcodec copy".split(" "), | ||
`${hash}-output.mp4`, | ||
]) | ||
|
||
// Retrieve the output file | ||
const mp4Data = await ffmpeg.readFile(`${hash}-output.mp4`) | ||
|
||
return mp4Data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { i18n } from "boot/i18n" | ||
import type PhimId from "src/apis/parser/phim/[id]" | ||
import type PhimIdChap from "src/apis/parser/phim/[id]/[chap]" | ||
|
||
import Worker from "./download-to-mp4.worker?worker" | ||
|
||
export function downloadToMp4( | ||
url: string, | ||
season: Awaited<ReturnType<typeof PhimId>>, | ||
chaps: Awaited<ReturnType<typeof PhimIdChap>>, | ||
currentChapId: string, | ||
onProgress: ( | ||
current: number, | ||
total: number, | ||
tCurrent: number, | ||
tDuration: number, | ||
speed: number | ||
) => void | ||
) { | ||
return new Promise<void>((resolve, reject) => { | ||
const worker = new Worker() | ||
worker.onmessage = ( | ||
event: MessageEvent< | ||
| { | ||
ok: boolean | ||
buffer: ArrayBuffer | ||
message?: string | ||
} | ||
| [ | ||
current: number, | ||
total: number, | ||
tCurrent: number, | ||
tDuration: number, | ||
speed: number | ||
] | ||
> | ||
) => { | ||
if ("ok" in event.data) { | ||
if (event.data.ok) { | ||
if (event.data.buffer) { | ||
// save to buffer | ||
const url = URL.createObjectURL( | ||
new Blob([event.data.buffer], { type: "video/mp4" }) | ||
) | ||
|
||
const a = document.createElement("a") | ||
a.href = url | ||
a.id = "temp_download" | ||
|
||
a.download = `[animevsub.eu.org] ${i18n.global.t( | ||
"tap-_chap-_name-_othername", | ||
[ | ||
chaps.chaps.find((item) => item.id === currentChapId)?.name, | ||
season.name, | ||
season.othername | ||
] | ||
)}.mp4` | ||
document.body.appendChild(a) | ||
a.click() | ||
|
||
// URL.revokeObjectURL(url) | ||
document.body.removeChild(a) | ||
} | ||
|
||
resolve() | ||
worker.terminate() | ||
} else reject(new Error(event.data.message ?? "")) | ||
} else { | ||
onProgress(...event.data) | ||
} | ||
} | ||
worker.onerror = (event) => reject(event) | ||
// worker.onmessageerror = (event) => reject(event) | ||
|
||
worker.postMessage({ | ||
url | ||
}) | ||
}) | ||
} |
Oops, something went wrong.