Merge remote-tracking branch 'origin/main' into widget-superpowered
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="4.3.8" date="2025-05-20" />
|
||||
<release version="4.2.4" date="2025-01-11" />
|
||||
<release version="4.0.3" date="2024-10-08" />
|
||||
</releases>
|
||||
@@ -33,4 +34,4 @@
|
||||
<color type="primary" scheme_preference="light">#ffffff</color>
|
||||
<color type="primary" scheme_preference="dark">#000000</color>
|
||||
</branding>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
@@ -5,15 +5,15 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
version: "72.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "0.3.2"
|
||||
adaptive_theme:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -26,10 +26,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "6.7.0"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -90,10 +90,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
version: "2.11.0"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -130,10 +130,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -202,10 +202,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -234,10 +234,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -250,10 +250,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
version: "1.18.0"
|
||||
confetti:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -435,10 +435,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -944,18 +944,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.8"
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1024,18 +1024,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1056,10 +1056,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.15.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1168,10 +1168,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.9.0"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1472,7 +1472,7 @@ packages:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
version: "0.0.99"
|
||||
sodium:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1509,10 +1509,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1566,10 +1566,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
version: "1.11.1"
|
||||
steam_totp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1590,10 +1590,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1606,10 +1606,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
version: "1.2.0"
|
||||
styled_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1630,18 +1630,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.2"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1806,10 +1806,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.1"
|
||||
version: "14.2.5"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1899,5 +1899,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
name: ente_auth
|
||||
description: ente two-factor authenticator
|
||||
version: 4.3.6+437
|
||||
version: 4.3.8+438
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
||||
70
auth/scripts/release_tag.sh
Executable file
70
auth/scripts/release_tag.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to display usage
|
||||
usage() {
|
||||
echo "Usage: $0 tag"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure a tag was provided
|
||||
[[ $# -eq 0 ]] && usage
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status
|
||||
set -e
|
||||
|
||||
# Go to the project root directory
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Get the tag from the command line argument
|
||||
TAG=$1
|
||||
|
||||
# Define the appdata file path - use absolute path to avoid directory navigation issues
|
||||
PROJECT_ROOT=$(pwd)
|
||||
APPDATA_FILE="${PROJECT_ROOT}/linux/packaging/enteauth.appdata.xml"
|
||||
|
||||
# Get the version from the pubspec.yaml file and cut everything after the +
|
||||
VERSION=$(grep "^version:" pubspec.yaml | awk '{ print $2 }' | cut -d '+' -f 1)
|
||||
|
||||
PREFIX="auth-v"
|
||||
|
||||
# Ensure the tag has the correct prefix
|
||||
if [[ $TAG != $PREFIX* ]]; then
|
||||
echo "Invalid tag. tags must start with '$PREFIX'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure the tag version is in the pubspec.yaml file
|
||||
if [[ $TAG != *$VERSION ]]; then
|
||||
echo "Invalid tag."
|
||||
echo "The version $VERSION in pubspec doesn't match the version in tag $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number from the tag (remove prefix)
|
||||
TAG_VERSION=${TAG#$PREFIX}
|
||||
|
||||
# Check if this version is already in the releases section of the appdata.xml file
|
||||
if ! grep -q "<release version=\"$TAG_VERSION\"" "$APPDATA_FILE"; then
|
||||
echo "Adding release entry for version $TAG_VERSION to appdata.xml"
|
||||
|
||||
# Get today's date in YYYY-MM-DD format
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
# Use a more reliable approach with awk instead of sed for cross-platform compatibility
|
||||
echo "Creating temporary file with updated content..."
|
||||
awk '/<releases>/{print $0; print " <release version=\"'"$TAG_VERSION"'\" date=\"'"$TODAY"'\" />"; next}1' "$APPDATA_FILE" > "${APPDATA_FILE}.tmp"
|
||||
mv "${APPDATA_FILE}.tmp" "$APPDATA_FILE"
|
||||
|
||||
echo "Added release entry for version $TAG_VERSION with date $TODAY"
|
||||
|
||||
# Stage and commit the updated appdata.xml file
|
||||
git add "$APPDATA_FILE"
|
||||
git commit -m "Add release $TAG_VERSION to appdata.xml"
|
||||
echo "Committed appdata.xml changes for version $TAG_VERSION"
|
||||
fi
|
||||
|
||||
# If all checks pass, create the tag
|
||||
git tag $TAG
|
||||
echo "Tag $TAG created."
|
||||
|
||||
exit 0
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
// See [Note: Using Electron APIs in UtilityProcess] about what we can and
|
||||
// cannot import.
|
||||
import shellescape from "any-shell-escape";
|
||||
import { expose } from "comlink";
|
||||
import pathToFfmpeg from "ffmpeg-static";
|
||||
import { randomBytes } from "node:crypto";
|
||||
@@ -324,6 +325,20 @@ const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
const playlistPath = path.join(outputPathPrefix, "output.m3u8");
|
||||
const videoPath = path.join(outputPathPrefix, "output.ts");
|
||||
|
||||
// A file into which we'll redirect ffmpeg's stderr.
|
||||
//
|
||||
// [Note: ERR_CHILD_PROCESS_STDIO_MAXBUFFER]
|
||||
//
|
||||
// For very large videos, the stderr output of ffmpeg may cause the stdio
|
||||
// max buffer size limits to be exceeded, raising the following error:
|
||||
//
|
||||
// RangeError [ERR_CHILD_PROCESS_STDIO_MAXBUFFER]: stderr maxBuffer length exceeded
|
||||
//
|
||||
// So instead of capturing the stderr normally, we redirect it to a
|
||||
// temporary file, and then read it from there to extract the video
|
||||
// dimensions.
|
||||
const stderrPath = path.join(outputPathPrefix, "stderr.txt");
|
||||
|
||||
// Generate a cryptographically secure random key (16 bytes).
|
||||
const keyBytes = randomBytes(16);
|
||||
const keyB64 = keyBytes.toString("base64");
|
||||
@@ -342,6 +357,18 @@ const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
// - the first line specifies the key URI that is written into the playlist.
|
||||
// - the second line specifies the path to the local file system file from
|
||||
// where ffmpeg should read the key.
|
||||
//
|
||||
// [Note: ffmpeg newlines]
|
||||
//
|
||||
// Tested on Windows that ffmpeg recognizes these lines correctly. In
|
||||
// general, ffmpeg tends to expect input and write output the Unix way (\n),
|
||||
// even when we're running on Windows.
|
||||
//
|
||||
// - The ffmetadata and the HLS playlist file generated by ffmpeg uses \n
|
||||
// separators, even on Windows.
|
||||
// - The HLS key info file, expected as an input by ffmpeg, works fine when
|
||||
// \n separated even on Windows.
|
||||
//
|
||||
const keyInfo = [keyURI, keyPath].join("\n");
|
||||
|
||||
// Overview:
|
||||
@@ -458,7 +485,7 @@ const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
playlistPath,
|
||||
].flat();
|
||||
|
||||
let dimensions: ReturnType<typeof detectVideoDimensions>;
|
||||
let dimensions: { width: number; height: number };
|
||||
let videoSize: number;
|
||||
|
||||
try {
|
||||
@@ -468,14 +495,26 @@ const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
fs.writeFile(keyInfoPath, keyInfo, { encoding: "utf8" }),
|
||||
]);
|
||||
|
||||
// Tack on the redirection after constructing the command.
|
||||
const commandWithRedirection = `${shellescape(command)} 2>${stderrPath}`;
|
||||
|
||||
// Run the ffmpeg command to generate the HLS playlist and segments.
|
||||
//
|
||||
// Note: Depending on the size of the input file, this may take long!
|
||||
const { stderr: conversionStderr } = await execAsyncWorker(command);
|
||||
await execAsyncWorker(commandWithRedirection);
|
||||
|
||||
// While ffmpeg uses \n as the line separator in the generated playlist
|
||||
// file on Windows too, add an extra safety check that should fail the
|
||||
// HLS generation if this doesn't hold. See: [Note: ffmpeg newlines].
|
||||
if (process.platform == "win32") {
|
||||
const playlistText = await fs.readFile(playlistPath, "utf-8");
|
||||
if (playlistText.includes("\r\n"))
|
||||
throw new Error("Unexpected Windows newlines in playlist");
|
||||
}
|
||||
|
||||
// Determine the dimensions of the generated video from the stderr
|
||||
// output produced by ffmpeg during the conversion.
|
||||
dimensions = detectVideoDimensions(conversionStderr);
|
||||
dimensions = await detectVideoDimensions(stderrPath);
|
||||
|
||||
// Find the size of the generated video segments by reading the size of
|
||||
// the generated .ts file.
|
||||
@@ -488,6 +527,7 @@ const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
throw e;
|
||||
} finally {
|
||||
await Promise.all([
|
||||
deletePathIgnoringErrors(stderrPath),
|
||||
deletePathIgnoringErrors(keyInfoPath),
|
||||
deletePathIgnoringErrors(keyPath),
|
||||
deletePathIgnoringErrors(videoPath),
|
||||
@@ -525,10 +565,10 @@ const deletePathIgnoringErrors = async (tempFilePath: string) => {
|
||||
*
|
||||
* Stream #0:1[0x2](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 480x270 [SAR 1:1 DAR 16:9], 539 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
|
||||
*/
|
||||
const videoStreamLineRegex = /Stream #.+: Video:(.+)\n/;
|
||||
const videoStreamLineRegex = /Stream #.+: Video:(.+)\r?\n/;
|
||||
|
||||
/** {@link videoStreamLineRegex}, but global. */
|
||||
const videoStreamLinesRegex = /Stream #.+: Video:(.+)\n/g;
|
||||
const videoStreamLinesRegex = /Stream #.+: Video:(.+)\r?\n/g;
|
||||
|
||||
/**
|
||||
* A regex that matches "<digits> kb/s" preceded by a space. See
|
||||
@@ -628,7 +668,12 @@ const detectVideoCharacteristics = async (inputFilePath: string) => {
|
||||
*
|
||||
* See: [Note: Parsing CLI output might break on ffmpeg updates].
|
||||
*/
|
||||
const detectVideoDimensions = (conversionStderr: string) => {
|
||||
const detectVideoDimensions = async (stderrPath: string) => {
|
||||
// Instead of reading the stderr directly off the child_process.exec, we
|
||||
// wrote it to a file to avoid hitting the max stdio buffer limits. Read it
|
||||
// from there.
|
||||
const conversionStderr = await fs.readFile(stderrPath, "utf-8");
|
||||
|
||||
// There is a nicer way to do it - by running `pseudoFFProbeVideo` on the
|
||||
// generated playlist. However, that playlist includes a data URL that
|
||||
// specifies the encryption info, and ffmpeg refuses to read that unless we
|
||||
|
||||
@@ -112,6 +112,7 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
_streamSwitchedSubscription =
|
||||
Bus.instance.on<StreamSwitchedEvent>().listen((event) {
|
||||
if (event.type != PlayerType.nativeVideoPlayer) return;
|
||||
_filePath = null;
|
||||
if (event.selectedPreview) {
|
||||
loadPreview(update: true);
|
||||
} else {
|
||||
@@ -133,6 +134,11 @@ class _VideoWidgetNativeState extends State<VideoWidgetNative>
|
||||
}
|
||||
|
||||
Future<void> setVideoSource() async {
|
||||
if (_filePath == null) {
|
||||
_logger.info('Stop video player, file path is null');
|
||||
await _controller?.stop();
|
||||
return;
|
||||
}
|
||||
final videoSource = VideoSource(
|
||||
path: _filePath!,
|
||||
type: VideoSourceType.file,
|
||||
|
||||
@@ -1010,7 +1010,7 @@ func cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", c.GetHeader("Origin"))
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, X-Auth-Token, X-Auth-Access-Token, X-Cast-Access-Token, X-Auth-Access-Token-JWT, X-Client-Package, X-Client-Version, Authorization, accept, origin, Cache-Control, X-Requested-With, upgrade-insecure-requests")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, X-Auth-Token, X-Auth-Access-Token, X-Cast-Access-Token, X-Auth-Access-Token-JWT, X-Client-Package, X-Client-Version, Authorization, accept, origin, Cache-Control, X-Requested-With, upgrade-insecure-requests, Range")
|
||||
c.Writer.Header().Set("Access-Control-Expose-Headers", "X-Request-Id")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE")
|
||||
c.Writer.Header().Set("Access-Control-Max-Age", "1728000")
|
||||
|
||||
@@ -29,8 +29,7 @@ func (c *Controller) InsertVideoPreview(ctx *gin.Context, req *filedata.VidPrevi
|
||||
if sizeErr := c.verifySize(bucketID, fileObjectKey, req.ObjectSize); sizeErr != nil {
|
||||
return stacktrace.Propagate(sizeErr, "failed to validate size")
|
||||
}
|
||||
// Start a goroutine to handle the upload and insert operations
|
||||
//go func() {
|
||||
|
||||
obj := filedata.S3FileMetadata{
|
||||
Version: *req.Version,
|
||||
EncryptedData: req.Playlist,
|
||||
@@ -59,9 +58,9 @@ func (c *Controller) InsertVideoPreview(ctx *gin.Context, req *filedata.VidPrevi
|
||||
dbInsertErr := c.Repo.InsertOrUpdatePreviewData(context.Background(), row, fileObjectKey)
|
||||
if dbInsertErr != nil {
|
||||
logger.WithError(dbInsertErr).Error("insert or update failed")
|
||||
return nil
|
||||
return stacktrace.Propagate(dbInsertErr, "failed to insert or update preview data")
|
||||
}
|
||||
//}()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@@ -582,9 +582,9 @@ const EmptyTrashQuickOption: React.FC<OptionProps> = ({ onClick }) => (
|
||||
);
|
||||
|
||||
const showDownloadQuickOption = (type: CollectionSummaryType) =>
|
||||
type == "album" ||
|
||||
type == "folder" ||
|
||||
type == "favorites" ||
|
||||
type == "album" ||
|
||||
type == "uncategorized" ||
|
||||
type == "hiddenItems" ||
|
||||
type == "incomingShareViewer" ||
|
||||
@@ -620,10 +620,10 @@ const DownloadQuickOption: React.FC<DownloadQuickOptionProps> = ({
|
||||
);
|
||||
|
||||
const showShareQuickOption = (type: CollectionSummaryType) =>
|
||||
type == "album" ||
|
||||
type == "folder" ||
|
||||
(type == "favorites" &&
|
||||
/* TODO(FAV): */ settingsSnapshot().isInternalUser) ||
|
||||
type == "album" ||
|
||||
type == "outgoingShare" ||
|
||||
type == "sharedOnlyViaLink" ||
|
||||
type == "archived" ||
|
||||
|
||||
@@ -184,8 +184,13 @@ const parseFFmpegExtractedMetadata = (ffmpegOutput: Uint8Array) => {
|
||||
// with comments and newlines.
|
||||
//
|
||||
// https://ffmpeg.org/ffmpeg-formats.html#Metadata-2
|
||||
//
|
||||
// On Windows, while I couldn't find it documented anywhere, the generated
|
||||
// ffmetadata file uses Unix line separators ("\n"). But for the sake of
|
||||
// extra (albeit possibly unnecessary) safety, handle both \r\n and \n
|
||||
// separators in the split. See: [Note: ffmpeg newlines]
|
||||
|
||||
const lines = new TextDecoder().decode(ffmpegOutput).split("\n");
|
||||
const lines = new TextDecoder().decode(ffmpegOutput).split(/\r?\n/);
|
||||
const isPair = (xs: string[]): xs is [string, string] => xs.length == 2;
|
||||
const kvPairs = lines.map((property) => property.split("=")).filter(isPair);
|
||||
|
||||
|
||||
@@ -6,14 +6,101 @@ import { ItemVisibility } from "ente-media/file-metadata";
|
||||
|
||||
// TODO: Audit this file
|
||||
|
||||
export type CollectionType = "folder" | "favorites" | "album" | "uncategorized";
|
||||
/**
|
||||
* The type of a collection.
|
||||
*
|
||||
* - "album" - A regular "Ente Album" that the user sees in their library.
|
||||
*
|
||||
* - "folder" - An Ente Album that is also associated with an OS album on the
|
||||
* user's mobile device.
|
||||
*
|
||||
* A collection of type "folder" is created by the mobile app if there is an
|
||||
* associated on-device album for the new Ente album being created.
|
||||
*
|
||||
* This separation between "album" and "folder" allows different mobile
|
||||
* clients to push to the same Folder ("Camera", "Screenshots"), not allowing
|
||||
* for duplicate folders with the same name, while still allowing users to
|
||||
* create different albums with the same name.
|
||||
*
|
||||
* The web/desktop app does not create collections of type "folder", and
|
||||
* otherwise treats them as aliases for "album".
|
||||
*
|
||||
* - "favorites" - A special collection consisting of the items that the user
|
||||
* has marked as their favorites.
|
||||
*
|
||||
* The user can have at most one collection of type "favorites" (enforced at
|
||||
* remote). This collection is created on demand by the client where the user
|
||||
* first marks an item as a favorite. The user can choose to share their
|
||||
* "favorites" with other users, so it is possible for there to be multiple
|
||||
* collections of type "favorites" present in our local database, however only
|
||||
* one of those will belong to the logged in user (cf `owner.id`).
|
||||
*
|
||||
* - "uncategorized" - A special collection consisting of items that do not
|
||||
* belong to any other collection.
|
||||
*
|
||||
* In the remote schema, each item ({@link EnteFile}) is always associated
|
||||
* with a collection. The same item may belong to multiple collections (See:
|
||||
* [Note: Collection File]), but it must belong to at least one collection.
|
||||
*
|
||||
* In some scenarios, e.g. when deleting the last collection to which a file
|
||||
* belongs, the file would thus get orphaned and violate the schema
|
||||
* invariants. So in such cases, the client which is performing the
|
||||
* corresponding operation moves the file to the user's special
|
||||
* "uncategorized" collection, creating it if needed.
|
||||
*
|
||||
* Similar to "favorites", the user can have only one "uncategorized"
|
||||
* collection. However, unlike "favorites", the "uncategorized" collection
|
||||
* cannot be shared.
|
||||
*/
|
||||
export type CollectionType = "album" | "folder" | "favorites" | "uncategorized";
|
||||
|
||||
export type CollectionRole = "VIEWER" | "OWNER" | "COLLABORATOR" | "UNKNOWN";
|
||||
/**
|
||||
* The privilege level of a participant associated with a collection.
|
||||
*
|
||||
* - "VIEWER" - Has read-only access to files in the collection.
|
||||
*
|
||||
* - "COLLABORATOR" - Can additionally add files from the collection, and remove
|
||||
* files that they added from the collection (i.e., files they "own").
|
||||
*
|
||||
* - "OWNER" - The owner of the collection. Can remove any file, including those
|
||||
* added by other users, from the collection.
|
||||
*
|
||||
* It is guaranteed that a there will be exactly one participant of type OWNER,
|
||||
* and their user ID will be the same as the collection `owner.id`.
|
||||
*/
|
||||
export type CollectionUserRole =
|
||||
| "VIEWER"
|
||||
| "COLLABORATOR"
|
||||
| "OWNER"
|
||||
| "UNKNOWN";
|
||||
|
||||
/**
|
||||
* Information about the user associated with a collection, either as an owner,
|
||||
* or as someone with whom the collection has been shared with.
|
||||
*/
|
||||
export interface CollectionUser {
|
||||
/**
|
||||
* The ID of the underlying {@link User} that this {@link CollectionUser}
|
||||
* stands for.
|
||||
*/
|
||||
id: number;
|
||||
email: string;
|
||||
role: CollectionRole;
|
||||
/**
|
||||
* The email of the user.
|
||||
*
|
||||
* - The email is present for the {@link owner} only for shared collections.
|
||||
* - The email is present for all {@link sharees}.
|
||||
* - Remote uses a blank string to indicate absent values.
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The association / privilege level of the user with the collection.
|
||||
*
|
||||
* - The role is not present blank for the {@link owner}.
|
||||
* - The role is present, and one of "VIEWER" and "COLLABORATOR" for the
|
||||
* {@link sharees}.
|
||||
* - Remote uses a blank string to indicate absent values.
|
||||
*/
|
||||
role?: CollectionUserRole;
|
||||
}
|
||||
|
||||
export interface EncryptedCollection {
|
||||
@@ -25,6 +112,13 @@ export interface EncryptedCollection {
|
||||
* all collections on an Ente instance (i.e., it is not scoped to a user).
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* Information about the user who owns the collection.
|
||||
*
|
||||
* Each collection is owned by exactly one user. The owner may optionally
|
||||
* choose to share it with additional users, granting them varying level of
|
||||
* privileges.
|
||||
*/
|
||||
owner: CollectionUser;
|
||||
// collection name was unencrypted in the past, so we need to keep it as optional
|
||||
name?: string;
|
||||
|
||||
@@ -52,7 +52,7 @@ export const constructUserIDToEmailMap = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (sharees) {
|
||||
sharees.forEach((item) => {
|
||||
if (item.id !== user.id)
|
||||
if (item.id !== user.id && item.email)
|
||||
userIDToEmailMap.set(item.id, item.email);
|
||||
});
|
||||
}
|
||||
@@ -78,10 +78,11 @@ export const createShareeSuggestionEmails = (
|
||||
// type for Collection.
|
||||
//
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return (sharees ?? []).map((sharee) => sharee.email);
|
||||
return (sharees ?? []).map(({ email }) => email);
|
||||
}
|
||||
})
|
||||
.flat();
|
||||
.flat()
|
||||
.filter((e) => e !== undefined);
|
||||
|
||||
// Add family members.
|
||||
if (familyData) {
|
||||
|
||||
@@ -1442,7 +1442,7 @@ const createCollectionSummaries = (
|
||||
// particular favorite as a prefix to disambiguate this collection
|
||||
// from the user's own favorites.
|
||||
// TODO(FAV): localize
|
||||
const initial = collection.owner.email.at(0)?.toUpperCase();
|
||||
const initial = collection.owner.email?.at(0)?.toUpperCase();
|
||||
if (initial) {
|
||||
// TODO(FAV):
|
||||
// name = `${initial}'s ${t("favorites")}`;
|
||||
|
||||
Reference in New Issue
Block a user