Pārlūkot izejas kodu

Create a package to share code between photos and cast

Manav Rathi 1 gadu atpakaļ
vecāks
revīzija
4b9446a9b0

+ 7 - 2
web/docs/dependencies.md

@@ -110,7 +110,7 @@ with Next.js.
 
 For more details, see [translations.md](translations.md).
 
-## Meta Frameworks
+## Meta frameworks
 
 ### Next.js
 
@@ -131,7 +131,12 @@ It is more lower level than Next, but the bells and whistles it doesn't have are
 the bells and whistles (and the accompanying complexity) that we don't need in
 some cases.
 
-## Photos
+## Media
+
+- "jszip" is used for reading zip files in JavaScript. Live photos are zip files
+  under the hood.
+
+## Photos app specific
 
 ### Misc
 

+ 3 - 0
web/packages/media/.eslintrc.js

@@ -0,0 +1,3 @@
+module.exports = {
+    extends: ["@/build-config/eslintrc-next"],
+};

+ 11 - 0
web/packages/media/README.md

@@ -0,0 +1,11 @@
+## @/media
+
+A package for sharing code between our apps that show media (photos, videos).
+
+Specifically, this is the intersection of code required by both the photos and
+cast apps.
+
+### Packaging
+
+This (internal) package exports a React TypeScript library. We rely on the
+importing project to transpile and bundle it.

+ 52 - 0
web/packages/media/live-photo.ts

@@ -0,0 +1,52 @@
+import JSZip from "jszip";
+
+class LivePhoto {
+    image: Uint8Array;
+    video: Uint8Array;
+    imageNameTitle: string;
+    videoNameTitle: string;
+}
+
+export function getFileNameWithoutExtension(filename: string) {
+    const lastDotPosition = filename.lastIndexOf(".");
+    if (lastDotPosition === -1) return filename;
+    else return filename.slice(0, lastDotPosition);
+}
+
+export function getFileExtensionWithDot(filename: string) {
+    const lastDotPosition = filename.lastIndexOf(".");
+    if (lastDotPosition === -1) return "";
+    else return filename.slice(lastDotPosition);
+}
+
+export const decodeLivePhoto = async (fileName: string, zipBlob: Blob) => {
+    const originalName = getFileNameWithoutExtension(fileName);
+    const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
+
+    const livePhoto = new LivePhoto();
+    for (const zipFilename in zip.files) {
+        if (zipFilename.startsWith("image")) {
+            livePhoto.imageNameTitle =
+                originalName + getFileExtensionWithDot(zipFilename);
+            livePhoto.image = await zip.files[zipFilename].async("uint8array");
+        } else if (zipFilename.startsWith("video")) {
+            livePhoto.videoNameTitle =
+                originalName + getFileExtensionWithDot(zipFilename);
+            livePhoto.video = await zip.files[zipFilename].async("uint8array");
+        }
+    }
+    return livePhoto;
+};
+
+export const encodeLivePhoto = async (livePhoto: LivePhoto) => {
+    const zip = new JSZip();
+    zip.file(
+        "image" + getFileExtensionWithDot(livePhoto.imageNameTitle),
+        livePhoto.image,
+    );
+    zip.file(
+        "video" + getFileExtensionWithDot(livePhoto.videoNameTitle),
+        livePhoto.video,
+    );
+    return await zip.generateAsync({ type: "uint8array" });
+};

+ 9 - 0
web/packages/media/package.json

@@ -0,0 +1,9 @@
+{
+    "name": "@/media",
+    "version": "0.0.0",
+    "private": true,
+    "dependencies": {
+        "@/next": "*",
+        "jszip": "^3.10"
+    }
+}

+ 5 - 0
web/packages/media/tsconfig.json

@@ -0,0 +1,5 @@
+{
+    "extends": "@/build-config/tsconfig-typecheck.json",
+    /* Typecheck all files with the given extensions (here or in subfolders) */
+    "include": ["**/*.ts", "**/*.tsx"]
+}

+ 1 - 1
web/yarn.lock

@@ -3252,7 +3252,7 @@ jssha@~3.3.1:
     object.assign "^4.1.4"
     object.values "^1.1.6"
 
-jszip@3.10.1:
+jszip@3.10.1, jszip@^3.10:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
   integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==