ashilkn 1 год назад
Родитель
Сommit
b494c308b1
45 измененных файлов с 670 добавлено и 267 удалено
  1. 0 1
      auth/lib/l10n/arb/app_en.arb
  2. 1 0
      auth/lib/main.dart
  3. 22 7
      auth/lib/models/code.dart
  4. 10 3
      auth/lib/onboarding/view/setup_enter_secret_key_page.dart
  5. 5 5
      auth/lib/ui/code_widget.dart
  6. 2 0
      auth/lib/ui/settings/data/import/bitwarden_import.dart
  7. 2 2
      auth/lib/utils/totp_util.dart
  8. 1 1
      auth/pubspec.yaml
  9. 6 13
      desktop/.github/workflows/desktop-release.yml
  10. 2 2
      desktop/docs/release.md
  11. 1 0
      desktop/electron-builder.yml
  12. 2 1
      desktop/package.json
  13. 482 87
      desktop/yarn.lock
  14. 3 3
      mobile/lib/generated/intl/messages_en.dart
  15. 6 6
      mobile/lib/generated/l10n.dart
  16. 2 2
      mobile/lib/l10n/intl_en.arb
  17. 1 1
      mobile/lib/services/update_service.dart
  18. 1 1
      mobile/lib/ui/cast/choose.dart
  19. 24 29
      mobile/lib/ui/notification/update/change_log_page.dart
  20. 7 1
      mobile/lib/utils/file_uploader.dart
  21. 1 1
      mobile/pubspec.yaml
  22. 1 2
      server/ente/cast/entity.go
  23. 6 6
      server/pkg/api/cast.go
  24. 1 3
      server/pkg/controller/cast/controller.go
  25. 2 29
      server/pkg/controller/storagebonus/referral.go
  26. 4 11
      server/pkg/repo/cast/repo.go
  27. 27 0
      server/pkg/utils/random/generate.go
  28. 2 4
      web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx
  29. 2 2
      web/packages/next/locales/bg-BG/translation.json
  30. 2 2
      web/packages/next/locales/de-DE/translation.json
  31. 4 4
      web/packages/next/locales/en-US/translation.json
  32. 2 2
      web/packages/next/locales/es-ES/translation.json
  33. 2 2
      web/packages/next/locales/fa-IR/translation.json
  34. 2 2
      web/packages/next/locales/fi-FI/translation.json
  35. 4 4
      web/packages/next/locales/fr-FR/translation.json
  36. 2 2
      web/packages/next/locales/it-IT/translation.json
  37. 2 2
      web/packages/next/locales/ko-KR/translation.json
  38. 4 4
      web/packages/next/locales/nl-NL/translation.json
  39. 4 4
      web/packages/next/locales/pt-BR/translation.json
  40. 2 2
      web/packages/next/locales/pt-PT/translation.json
  41. 4 4
      web/packages/next/locales/ru-RU/translation.json
  42. 2 2
      web/packages/next/locales/sv-SE/translation.json
  43. 2 2
      web/packages/next/locales/th-TH/translation.json
  44. 2 2
      web/packages/next/locales/tr-TR/translation.json
  45. 4 4
      web/packages/next/locales/zh-CN/translation.json

+ 0 - 1
auth/lib/l10n/arb/app_en.arb

@@ -20,7 +20,6 @@
   "codeIssuerHint": "Issuer",
   "codeSecretKeyHint": "Secret Key",
   "codeAccountHint": "Account (you@domain.com)",
-  "accountKeyType": "Type of key",
   "sessionExpired": "Session expired",
   "@sessionExpired": {
     "description": "Title of the dialog when the users current session is invalid/expired"

+ 1 - 0
auth/lib/main.dart

@@ -37,6 +37,7 @@ import 'package:window_manager/window_manager.dart';
 final _logger = Logger("main");
 
 Future<void> initSystemTray() async {
+  if (PlatformUtil.isMobile()) return;
   String path = Platform.isWindows
       ? 'assets/icons/auth-icon.ico'
       : 'assets/icons/auth-icon.png';

+ 22 - 7
auth/lib/models/code.dart

@@ -2,6 +2,7 @@ import 'package:ente_auth/utils/totp_util.dart';
 
 class Code {
   static const defaultDigits = 6;
+  static const steamDigits = 5;
   static const defaultPeriod = 30;
 
   int? generatedID;
@@ -57,36 +58,42 @@ class Code {
       updatedAlgo,
       updatedType,
       updatedCounter,
-      "otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=$updateIssuer&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
+      "otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}"
+      "&digits=$updatedDigits&issuer=$updateIssuer"
+      "&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
       generatedID: generatedID,
     );
   }
 
   static Code fromAccountAndSecret(
+    Type type,
     String account,
     String issuer,
     String secret,
+    int digits,
   ) {
     return Code(
       account,
       issuer,
-      defaultDigits,
+      digits,
       defaultPeriod,
       secret,
       Algorithm.sha1,
-      Type.totp,
+      type,
       0,
-      "otpauth://totp/$issuer:$account?algorithm=SHA1&digits=6&issuer=$issuer&period=30&secret=$secret",
+      "otpauth://${type.name}/$issuer:$account?algorithm=SHA1&digits=$digits&issuer=$issuer&period=30&secret=$secret",
     );
   }
 
   static Code fromRawData(String rawData) {
     Uri uri = Uri.parse(rawData);
+    final issuer = _getIssuer(uri);
+
     try {
       return Code(
         _getAccount(uri),
-        _getIssuer(uri),
-        _getDigits(uri),
+        issuer,
+        _getDigits(uri, issuer),
         _getPeriod(uri),
         getSanitizedSecret(uri.queryParameters['secret']!),
         _getAlgorithm(uri),
@@ -140,10 +147,13 @@ class Code {
     }
   }
 
-  static int _getDigits(Uri uri) {
+  static int _getDigits(Uri uri, String issuer) {
     try {
       return int.parse(uri.queryParameters['digits']!);
     } catch (e) {
+      if (issuer.toLowerCase() == "steam") {
+        return steamDigits;
+      }
       return defaultDigits;
     }
   }
@@ -186,6 +196,8 @@ class Code {
   static Type _getType(Uri uri) {
     if (uri.host == "totp") {
       return Type.totp;
+    } else if (uri.host == "steam") {
+      return Type.steam;
     } else if (uri.host == "hotp") {
       return Type.hotp;
     }
@@ -223,6 +235,9 @@ class Code {
 enum Type {
   totp,
   hotp,
+  steam;
+
+  bool get isTOTPCompatible => this == totp || this == steam;
 }
 
 enum Algorithm {

+ 10 - 3
auth/lib/onboarding/view/setup_enter_secret_key_page.dart

@@ -61,6 +61,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
                   },
                   decoration: InputDecoration(
                     hintText: l10n.codeIssuerHint,
+                    floatingLabelBehavior: FloatingLabelBehavior.auto,
+                    labelText: l10n.codeIssuerHint,
                   ),
                   controller: _issuerController,
                   autofocus: true,
@@ -78,6 +80,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
                   },
                   decoration: InputDecoration(
                     hintText: l10n.codeSecretKeyHint,
+                    floatingLabelBehavior: FloatingLabelBehavior.auto,
+                    labelText: l10n.codeSecretKeyHint,
                     suffixIcon: IconButton(
                       onPressed: () {
                         setState(() {
@@ -105,12 +109,12 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
                   },
                   decoration: InputDecoration(
                     hintText: l10n.codeAccountHint,
+                    floatingLabelBehavior: FloatingLabelBehavior.auto,
+                    labelText: l10n.codeAccountHint,
                   ),
                   controller: _accountController,
                 ),
-                const SizedBox(
-                  height: 40,
-                ),
+                const SizedBox(height: 40),
                 SizedBox(
                   width: 400,
                   child: OutlinedButton(
@@ -152,6 +156,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
       final account = _accountController.text.trim();
       final issuer = _issuerController.text.trim();
       final secret = _secretController.text.trim().replaceAll(' ', '');
+      final isStreamCode = issuer.toLowerCase() == "steam";
       if (widget.code != null && widget.code!.secret != secret) {
         ButtonResult? result = await showChoiceActionSheet(
           context,
@@ -168,9 +173,11 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
       }
       final Code newCode = widget.code == null
           ? Code.fromAccountAndSecret(
+              isStreamCode ? Type.steam : Type.totp,
               account,
               issuer,
               secret,
+              isStreamCode ? Code.steamDigits : Code.defaultDigits,
             )
           : widget.code!.copyWith(
               account: account,

+ 5 - 5
auth/lib/ui/code_widget.dart

@@ -53,7 +53,7 @@ class _CodeWidgetState extends State<CodeWidget> {
       String newCode = _getCurrentOTP();
       if (newCode != _currentCode.value) {
         _currentCode.value = newCode;
-        if (widget.code.type == Type.totp) {
+        if (widget.code.type.isTOTPCompatible) {
           _nextCode.value = _getNextTotp();
         }
       }
@@ -78,7 +78,7 @@ class _CodeWidgetState extends State<CodeWidget> {
     _shouldShowLargeIcon = PreferenceService.instance.shouldShowLargeIcons();
     if (!_isInitialized) {
       _currentCode.value = _getCurrentOTP();
-      if (widget.code.type == Type.totp) {
+      if (widget.code.type.isTOTPCompatible) {
         _nextCode.value = _getNextTotp();
       }
       _isInitialized = true;
@@ -213,7 +213,7 @@ class _CodeWidgetState extends State<CodeWidget> {
         crossAxisAlignment: CrossAxisAlignment.start,
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
-          if (widget.code.type == Type.totp)
+          if (widget.code.type.isTOTPCompatible)
             CodeTimerProgress(
               period: widget.code.period,
             ),
@@ -263,7 +263,7 @@ class _CodeWidgetState extends State<CodeWidget> {
               },
             ),
           ),
-          widget.code.type == Type.totp
+          widget.code.type.isTOTPCompatible
               ? GestureDetector(
                   onTap: () {
                     _copyNextToClipboard();
@@ -481,7 +481,7 @@ class _CodeWidgetState extends State<CodeWidget> {
 
   String _getNextTotp() {
     try {
-      assert(widget.code.type == Type.totp);
+      assert(widget.code.type.isTOTPCompatible);
       return getNextTotp(widget.code);
     } catch (e) {
       return context.l10n.error;

+ 2 - 0
auth/lib/ui/settings/data/import/bitwarden_import.dart

@@ -92,9 +92,11 @@ Future<int?> _processBitwardenExportFile(
         var account = item['login']['username'];
 
         code = Code.fromAccountAndSecret(
+          Type.totp,
           account,
           issuer,
           totp,
+          Code.defaultDigits,
         );
       }
 

+ 2 - 2
auth/lib/utils/totp_util.dart

@@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
 import 'package:otp/otp.dart' as otp;
 
 String getOTP(Code code) {
-  if(code.type == Type.hotp) {
+  if (code.type == Type.hotp) {
     return _getHOTPCode(code);
   }
   return otp.OTP.generateTOTPCodeString(
@@ -60,4 +60,4 @@ String safeDecode(String value) {
     debugPrint("Failed to decode $e");
     return value;
   }
-}
+}

+ 1 - 1
auth/pubspec.yaml

@@ -1,6 +1,6 @@
 name: ente_auth
 description: ente two-factor authenticator
-version: 2.0.56+256
+version: 2.0.57+257
 publish_to: none
 
 environment:

+ 6 - 13
desktop/.github/workflows/desktop-release.yml

@@ -32,9 +32,7 @@ jobs:
 
         strategy:
             matrix:
-                os: [macos-latest]
-                # Commented for testing
-                # os: [macos-latest, ubuntu-latest, windows-latest]
+                os: [macos-latest, ubuntu-latest, windows-latest]
 
         steps:
             - name: Checkout code
@@ -55,13 +53,6 @@ jobs:
             - name: Install dependencies
               run: yarn install
 
-            - name: Prepare for app notarization
-              if: startsWith(matrix.os, 'macos')
-              # Import Apple API key for app notarization on macOS
-              run: |
-                  mkdir -p ~/private_keys/
-                  echo '${{ secrets.API_KEY }}' > ~/private_keys/AuthKey_${{ secrets.API_KEY_ID }}.p8
-
             - name: Install libarchive-tools for pacman build
               if: startsWith(matrix.os, 'ubuntu')
               # See:
@@ -84,7 +75,9 @@ jobs:
                   mac_certs: ${{ secrets.MAC_CERTS }}
                   mac_certs_password: ${{ secrets.MAC_CERTS_PASSWORD }}
               env:
-                  # macOS notarization API key details
-                  API_KEY_ID: ${{ secrets.API_KEY_ID }}
-                  API_KEY_ISSUER_ID: ${{ secrets.API_KEY_ISSUER_ID }}
+                  # macOS notarization credentials key details
+                  APPLE_ID: ${{ secrets.APPLE_ID }}
+                  APPLE_APP_SPECIFIC_PASSWORD:
+                      ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
+                  APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
                   USE_HARD_LINKS: false

+ 2 - 2
desktop/docs/release.md

@@ -23,10 +23,10 @@ The workflow is:
 
     -   Update the CHANGELOG.
     -   Update the version in `package.json`
-    -   `git commit -m 'Release v1.x.x'`
+    -   `git commit -m "[photosd] Release v1.2.3"`
     -   Open PR, merge into main.
 
-2.  Tag this commit with a tag matching the pattern `photosd-v1.2.3`, where
+2.  Tag the merge commit with a tag matching the pattern `photosd-v1.2.3`, where
     `1.2.3` is the version in `package.json`
 
     ```sh

+ 1 - 0
desktop/electron-builder.yml

@@ -29,4 +29,5 @@ mac:
         arch: [universal]
     category: public.app-category.photography
     hardenedRuntime: true
+    notarize: true
 afterSign: electron-builder-notarize

+ 2 - 1
desktop/package.json

@@ -3,6 +3,7 @@
     "version": "1.7.0-beta.0",
     "private": true,
     "description": "Desktop client for Ente Photos",
+    "repository": "github:ente-io/photos-desktop",
     "author": "Ente <code@ente.io>",
     "main": "app/main.js",
     "scripts": {
@@ -44,7 +45,7 @@
         "@typescript-eslint/parser": "^7",
         "concurrently": "^8",
         "electron": "^30",
-        "electron-builder": "^24",
+        "electron-builder": "25.0.0-alpha.6",
         "electron-builder-notarize": "^1.5",
         "eslint": "^8",
         "prettier": "^3",

Разница между файлами не показана из-за своего большого размера
+ 482 - 87
desktop/yarn.lock


+ 3 - 3
mobile/lib/generated/intl/messages_en.dart

@@ -362,8 +362,8 @@ class MessageLookup extends MessageLookupByLibrary {
         "autoCastiOSPermission": MessageLookupByLibrary.simpleMessage(
             "Make sure Local Network permissions are turned on for the Ente Photos app, in Settings."),
         "autoPair": MessageLookupByLibrary.simpleMessage("Auto pair"),
-        "autoPairGoogle": MessageLookupByLibrary.simpleMessage(
-            "Auto Pair requires connecting to Google servers and only works with Chromecast supported devices. Google will not receive sensitive data, such as your photos."),
+        "autoPairDesc": MessageLookupByLibrary.simpleMessage(
+            "Auto pair works only with devices that support Chromecast."),
         "available": MessageLookupByLibrary.simpleMessage("Available"),
         "backedUpFolders":
             MessageLookupByLibrary.simpleMessage("Backed up folders"),
@@ -918,7 +918,7 @@ class MessageLookup extends MessageLookupByLibrary {
         "manageSubscription":
             MessageLookupByLibrary.simpleMessage("Manage subscription"),
         "manualPairDesc": MessageLookupByLibrary.simpleMessage(
-            "Pair with PIN works for any large screen device you want to play your album on."),
+            "Pair with PIN works with any screen you wish to view your album on."),
         "map": MessageLookupByLibrary.simpleMessage("Map"),
         "maps": MessageLookupByLibrary.simpleMessage("Maps"),
         "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"),

+ 6 - 6
mobile/lib/generated/l10n.dart

@@ -8594,20 +8594,20 @@ class S {
     );
   }
 
-  /// `Auto Pair requires connecting to Google servers and only works with Chromecast supported devices. Google will not receive sensitive data, such as your photos.`
-  String get autoPairGoogle {
+  /// `Auto pair works only with devices that support Chromecast.`
+  String get autoPairDesc {
     return Intl.message(
-      'Auto Pair requires connecting to Google servers and only works with Chromecast supported devices. Google will not receive sensitive data, such as your photos.',
-      name: 'autoPairGoogle',
+      'Auto pair works only with devices that support Chromecast.',
+      name: 'autoPairDesc',
       desc: '',
       args: [],
     );
   }
 
-  /// `Pair with PIN works for any large screen device you want to play your album on.`
+  /// `Pair with PIN works with any screen you wish to view your album on.`
   String get manualPairDesc {
     return Intl.message(
-      'Pair with PIN works for any large screen device you want to play your album on.',
+      'Pair with PIN works with any screen you wish to view your album on.',
       name: 'manualPairDesc',
       desc: '',
       args: [],

+ 2 - 2
mobile/lib/l10n/intl_en.arb

@@ -1216,8 +1216,8 @@
   "customEndpoint": "Connected to {endpoint}",
   "createCollaborativeLink": "Create collaborative link",
   "search": "Search",
-  "autoPairGoogle": "Auto Pair requires connecting to Google servers and only works with Chromecast supported devices. Google will not receive sensitive data, such as your photos.",
-  "manualPairDesc": "Pair with PIN works for any large screen device you want to play your album on.",
+  "autoPairDesc": "Auto pair works only with devices that support Chromecast.",
+  "manualPairDesc": "Pair with PIN works with any screen you wish to view your album on.",
   "connectToDevice": "Connect to device",
   "autoCastDialogBody": "You'll see available Cast devices here.",
   "autoCastiOSPermission": "Make sure Local Network permissions are turned on for the Ente Photos app, in Settings.",

+ 1 - 1
mobile/lib/services/update_service.dart

@@ -16,7 +16,7 @@ class UpdateService {
   static final UpdateService instance = UpdateService._privateConstructor();
   static const kUpdateAvailableShownTimeKey = "update_available_shown_time_key";
   static const changeLogVersionKey = "update_change_log_key";
-  static const currentChangeLogVersion = 18;
+  static const currentChangeLogVersion = 19;
 
   LatestVersionInfo? _latestVersion;
   final _logger = Logger("UpdateService");

+ 1 - 1
mobile/lib/ui/cast/choose.dart

@@ -31,7 +31,7 @@ class _CastChooseDialogState extends State<CastChooseDialog> {
         children: [
           const SizedBox(height: 8),
           Text(
-            S.of(context).autoPairGoogle,
+            S.of(context).autoPairDesc,
             style: textStyle.bodyMuted,
           ),
           const SizedBox(height: 12),

+ 24 - 29
mobile/lib/ui/notification/update/change_log_page.dart

@@ -1,5 +1,3 @@
-import "dart:async";
-
 import 'package:flutter/material.dart';
 import "package:photos/generated/l10n.dart";
 import 'package:photos/services/update_service.dart';
@@ -9,7 +7,6 @@ import 'package:photos/ui/components/divider_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/components/title_bar_title_widget.dart';
 import 'package:photos/ui/notification/update/change_log_entry.dart';
-import "package:url_launcher/url_launcher_string.dart";
 
 class ChangeLogPage extends StatefulWidget {
   const ChangeLogPage({
@@ -81,31 +78,31 @@ class _ChangeLogPageState extends State<ChangeLogPage> {
                     const SizedBox(
                       height: 8,
                     ),
-                    ButtonWidget(
-                      buttonType: ButtonType.trailingIconSecondary,
-                      buttonSize: ButtonSize.large,
-                      labelText: S.of(context).joinDiscord,
-                      icon: Icons.discord_outlined,
-                      iconColor: enteColorScheme.primary500,
-                      onTap: () async {
-                        unawaited(
-                          launchUrlString(
-                            "https://discord.com/invite/z2YVKkycX3",
-                            mode: LaunchMode.externalApplication,
-                          ),
-                        );
-                      },
-                    ),
                     // ButtonWidget(
                     //   buttonType: ButtonType.trailingIconSecondary,
                     //   buttonSize: ButtonSize.large,
-                    //   labelText: S.of(context).rateTheApp,
-                    //   icon: Icons.favorite_rounded,
+                    //   labelText: S.of(context).joinDiscord,
+                    //   icon: Icons.discord_outlined,
                     //   iconColor: enteColorScheme.primary500,
                     //   onTap: () async {
-                    //     await UpdateService.instance.launchReviewUrl();
+                    //     unawaited(
+                    //       launchUrlString(
+                    //         "https://discord.com/invite/z2YVKkycX3",
+                    //         mode: LaunchMode.externalApplication,
+                    //       ),
+                    //     );
                     //   },
                     // ),
+                    ButtonWidget(
+                      buttonType: ButtonType.trailingIconSecondary,
+                      buttonSize: ButtonSize.large,
+                      labelText: S.of(context).rateTheApp,
+                      icon: Icons.favorite_rounded,
+                      iconColor: enteColorScheme.primary500,
+                      onTap: () async {
+                        await UpdateService.instance.launchReviewUrl();
+                      },
+                    ),
                     const SizedBox(height: 8),
                   ],
                 ),
@@ -122,18 +119,16 @@ class _ChangeLogPageState extends State<ChangeLogPage> {
     final List<ChangeLogEntry> items = [];
     items.addAll([
       ChangeLogEntry(
-        "Improved Performance for Large Galleries ✨",
-        'We\'ve made significant improvements to how quickly galleries load and'
-            ' with less stutter, especially for those with a lot of photos and videos.',
+        "Cast albums to TV ✨",
+        "View a slideshow of your albums on any big screen! Open an album and click on the Cast button to get started.",
       ),
       ChangeLogEntry(
-        "Enhanced Functionality for Video Backups",
-        'Even if video backups are disabled, you can now manually upload individual videos.',
+        "Own shared photos",
+        "You can now add shared items to your favorites to any of your personal albums. Ente will create a copy that is fully owned by you and can be organized to your liking.",
       ),
       ChangeLogEntry(
-        "Bug Fixes",
-        'Many a bugs were squashed in this release.\n'
-            '\nIf you run into any, please write to team@ente.io, or let us know on Discord! 🙏',
+        "Performance improvements",
+        "This release also brings in major changes that should improve responsiveness. If you discover room for improvement, please let us know!",
       ),
     ]);
 

+ 7 - 1
mobile/lib/utils/file_uploader.dart

@@ -376,7 +376,13 @@ class FileUploader {
     if (Platform.isAndroid) {
       final bool hasPermission = await Permission.accessMediaLocation.isGranted;
       if (!hasPermission) {
-        throw NoMediaLocationAccessError();
+        final permissionStatus = await Permission.accessMediaLocation.request();
+        if (!permissionStatus.isGranted) {
+          _logger.severe(
+            "Media location access denied with permission status: ${permissionStatus.name}",
+          );
+          throw NoMediaLocationAccessError();
+        }
       }
     }
   }

+ 1 - 1
mobile/pubspec.yaml

@@ -12,7 +12,7 @@ description: ente photos application
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 
-version: 0.8.89+609
+version: 0.8.90+610
 publish_to: none
 
 environment:

+ 1 - 2
server/ente/cast/entity.go

@@ -9,8 +9,7 @@ type CastRequest struct {
 }
 
 type RegisterDeviceRequest struct {
-	DeviceCode *string `json:"deviceCode"`
-	PublicKey  string  `json:"publicKey" binding:"required"`
+	PublicKey string `json:"publicKey" binding:"required"`
 }
 
 type AuthContext struct {

+ 6 - 6
server/pkg/api/cast.go

@@ -1,16 +1,16 @@
 package api
 
 import (
-	entity "github.com/ente-io/museum/ente/cast"
-	"github.com/ente-io/museum/pkg/controller/cast"
-	"net/http"
-	"strconv"
-
 	"github.com/ente-io/museum/ente"
+	entity "github.com/ente-io/museum/ente/cast"
 	"github.com/ente-io/museum/pkg/controller"
+	"github.com/ente-io/museum/pkg/controller/cast"
 	"github.com/ente-io/museum/pkg/utils/handler"
 	"github.com/ente-io/stacktrace"
 	"github.com/gin-gonic/gin"
+	"net/http"
+	"strconv"
+	"strings"
 )
 
 // CastHandler exposes request handlers for publicly accessible collections
@@ -126,7 +126,7 @@ func (h *CastHandler) GetDiff(c *gin.Context) {
 }
 
 func getDeviceCode(c *gin.Context) string {
-	return c.Param("deviceCode")
+	return strings.ToUpper(c.Param("deviceCode"))
 }
 
 func (h *CastHandler) getFileForType(c *gin.Context, objectType ente.ObjectType) {

+ 1 - 3
server/pkg/controller/cast/controller.go

@@ -2,7 +2,6 @@ package cast
 
 import (
 	"context"
-	"github.com/ente-io/museum/ente"
 	"github.com/ente-io/museum/ente/cast"
 	"github.com/ente-io/museum/pkg/controller/access"
 	castRepo "github.com/ente-io/museum/pkg/repo/cast"
@@ -28,7 +27,7 @@ func NewController(castRepo *castRepo.Repository,
 }
 
 func (c *Controller) RegisterDevice(ctx *gin.Context, request *cast.RegisterDeviceRequest) (string, error) {
-	return c.CastRepo.AddCode(ctx, request.DeviceCode, request.PublicKey, network.GetClientIP(ctx))
+	return c.CastRepo.AddCode(ctx, request.PublicKey, network.GetClientIP(ctx))
 }
 
 func (c *Controller) GetPublicKey(ctx *gin.Context, deviceCode string) (string, error) {
@@ -42,7 +41,6 @@ func (c *Controller) GetPublicKey(ctx *gin.Context, deviceCode string) (string,
 			"ip":         ip,
 			"clientIP":   network.GetClientIP(ctx),
 		}).Warn("GetPublicKey: IP mismatch")
-		return "", &ente.ErrCastIPMismatch
 	}
 	return pubKey, nil
 }

+ 2 - 29
server/pkg/controller/storagebonus/referral.go

@@ -3,7 +3,7 @@ package storagebonus
 import (
 	"database/sql"
 	"errors"
-	"fmt"
+	"github.com/ente-io/museum/pkg/utils/random"
 
 	"github.com/ente-io/museum/ente"
 	entity "github.com/ente-io/museum/ente/storagebonus"
@@ -119,7 +119,7 @@ func (c *Controller) GetOrCreateReferralCode(ctx *gin.Context, userID int64) (*s
 		if !errors.Is(err, sql.ErrNoRows) {
 			return nil, stacktrace.Propagate(err, "failed to get storagebonus code")
 		}
-		code, err := generateAlphaNumString(codeLength)
+		code, err := random.GenerateAlphaNumString(codeLength)
 		if err != nil {
 			return nil, stacktrace.Propagate(err, "")
 		}
@@ -131,30 +131,3 @@ func (c *Controller) GetOrCreateReferralCode(ctx *gin.Context, userID int64) (*s
 	}
 	return referralCode, nil
 }
-
-// generateAlphaNumString returns AlphaNumeric code of given length
-// which exclude number 0 and letter O. The code always starts with an
-// alphabet
-func generateAlphaNumString(length int) (string, error) {
-	// Define the alphabet and numbers to be used in the string.
-	alphabet := "ABCDEFGHIJKLMNPQRSTUVWXYZ"
-	// Define the alphabet and numbers to be used in the string.
-	alphaNum := fmt.Sprintf("%s123456789", alphabet)
-	// Allocate a byte slice with the desired length.
-	result := make([]byte, length)
-	// Generate the first letter as an alphabet.
-	r0, err := auth.GenerateRandomInt(int64(len(alphabet)))
-	if err != nil {
-		return "", stacktrace.Propagate(err, "")
-	}
-	result[0] = alphabet[r0]
-	// Generate the remaining characters as alphanumeric.
-	for i := 1; i < length; i++ {
-		ri, err := auth.GenerateRandomInt(int64(len(alphaNum)))
-		if err != nil {
-			return "", stacktrace.Propagate(err, "")
-		}
-		result[i] = alphaNum[ri]
-	}
-	return string(result), nil
-}

+ 4 - 11
server/pkg/repo/cast/repo.go

@@ -8,23 +8,16 @@ import (
 	"github.com/ente-io/stacktrace"
 	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
-	"strings"
 )
 
 type Repository struct {
 	DB *sql.DB
 }
 
-func (r *Repository) AddCode(ctx context.Context, code *string, pubKey string, ip string) (string, error) {
-	var codeValue string
-	var err error
-	if code == nil || *code == "" {
-		codeValue, err = random.GenerateSixDigitOtp()
-		if err != nil {
-			return "", stacktrace.Propagate(err, "")
-		}
-	} else {
-		codeValue = strings.TrimSpace(*code)
+func (r *Repository) AddCode(ctx context.Context, pubKey string, ip string) (string, error) {
+	codeValue, err := random.GenerateAlphaNumString(6)
+	if err != nil {
+		return "", err
 	}
 	_, err = r.DB.ExecContext(ctx, "INSERT INTO casting (code, public_key, id, ip) VALUES ($1, $2, $3, $4)", codeValue, pubKey, uuid.New(), ip)
 	if err != nil {

+ 27 - 0
server/pkg/utils/random/generate.go

@@ -13,3 +13,30 @@ func GenerateSixDigitOtp() (string, error) {
 	}
 	return fmt.Sprintf("%06d", n), nil
 }
+
+// GenerateAlphaNumString returns AlphaNumeric code of given length
+// which exclude number 0 and letter O. The code always starts with an
+// alphabet
+func GenerateAlphaNumString(length int) (string, error) {
+	// Define the alphabet and numbers to be used in the string.
+	alphabet := "ABCDEFGHIJKLMNPQRSTUVWXYZ"
+	// Define the alphabet and numbers to be used in the string.
+	alphaNum := fmt.Sprintf("%s123456789", alphabet)
+	// Allocate a byte slice with the desired length.
+	result := make([]byte, length)
+	// Generate the first letter as an alphabet.
+	r0, err := auth.GenerateRandomInt(int64(len(alphabet)))
+	if err != nil {
+		return "", stacktrace.Propagate(err, "")
+	}
+	result[0] = alphabet[r0]
+	// Generate the remaining characters as alphanumeric.
+	for i := 1; i < length; i++ {
+		ri, err := auth.GenerateRandomInt(int64(len(alphaNum)))
+		if err != nil {
+			return "", stacktrace.Propagate(err, "")
+		}
+		result[i] = alphaNum[ri]
+	}
+	return string(result), nil
+}

+ 2 - 4
web/apps/photos/src/components/Collections/CollectionOptions/AlbumCastDialog.tsx

@@ -161,9 +161,7 @@ export default function AlbumCastDialog(props: Props) {
                     {browserCanCast && (
                         <>
                             <Typography color={"text.muted"}>
-                                {t(
-                                    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE",
-                                )}
+                                {t("AUTO_CAST_PAIR_DESC")}
                             </Typography>
 
                             <EnteButton
@@ -179,7 +177,7 @@ export default function AlbumCastDialog(props: Props) {
                         </>
                     )}
                     <Typography color="text.muted">
-                        {t("PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE")}
+                        {t("PAIR_WITH_PIN_DESC")}
                     </Typography>
 
                     <EnteButton

+ 2 - 2
web/packages/next/locales/bg-BG/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/de-DE/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "Geräte koppeln",
     "TV_NOT_FOUND": "Fernseher nicht gefunden. Hast du die PIN korrekt eingegeben?",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "Freihand",

+ 4 - 4
web/packages/next/locales/en-US/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "Enter the code you see on the TV below to pair this device.",
     "PAIR_DEVICE_TO_TV": "Pair devices",
     "TV_NOT_FOUND": "TV not found. Did you enter the PIN correctly?",
-    "AUTO_CAST_PAIR": "Auto Pair",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "Auto Pair requires connecting to Google servers and only works with Chromecast supported devices. Google will not receive sensitive data, such as your photos.",
+    "AUTO_CAST_PAIR": "Auto pair",
+    "AUTO_CAST_PAIR_DESC": "Auto pair works only with devices that support Chromecast.",
     "PAIR_WITH_PIN": "Pair with PIN",
     "CHOOSE_DEVICE_FROM_BROWSER": "Choose a cast-compatible device from the browser popup.",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "Pair with PIN works for any large screen device you want to play your album on.",
+    "PAIR_WITH_PIN_DESC": "Pair with PIN works with any screen you wish to view your album on.",
     "VISIT_CAST_ENTE_IO": "Visit <a>{{url}}</a> on the device you want to pair.",
-    "CAST_AUTO_PAIR_FAILED": "Chromecast Auto Pair failed. Please try again.",
+    "CAST_AUTO_PAIR_FAILED": "Chromecast auto pair failed. Please try again.",
     "FREEHAND": "Freehand",
     "APPLY_CROP": "Apply Crop",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "At least one transformation or color adjustment must be performed before saving.",

+ 2 - 2
web/packages/next/locales/es-ES/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/fa-IR/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/fi-FI/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 4 - 4
web/packages/next/locales/fr-FR/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "Entrez le code que vous voyez sur la TV ci-dessous pour appairer cet appareil.",
     "PAIR_DEVICE_TO_TV": "Associer les appareils",
     "TV_NOT_FOUND": "TV introuvable. Avez-vous entré le code PIN correctement ?",
-    "AUTO_CAST_PAIR": "Paire automatique",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "La paire automatique nécessite la connexion aux serveurs Google et ne fonctionne qu'avec les appareils pris en charge par Chromecast. Google ne recevra pas de données sensibles, telles que vos photos.",
+    "AUTO_CAST_PAIR": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "Associer avec le code PIN",
     "CHOOSE_DEVICE_FROM_BROWSER": "Choisissez un périphérique compatible avec la caste à partir de la fenêtre pop-up du navigateur.",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "L'association avec le code PIN fonctionne pour tout appareil grand écran sur lequel vous voulez lire votre album.",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "Visitez <a>{{url}}</a> sur l'appareil que vous voulez associer.",
-    "CAST_AUTO_PAIR_FAILED": "La paire automatique de Chromecast a échoué. Veuillez réessayer.",
+    "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "Main levée",
     "APPLY_CROP": "Appliquer le recadrage",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "Au moins une transformation ou un ajustement de couleur doit être effectué avant de sauvegarder.",

+ 2 - 2
web/packages/next/locales/it-IT/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/ko-KR/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 4 - 4
web/packages/next/locales/nl-NL/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "Voer de code in die u op de TV ziet om dit apparaat te koppelen.",
     "PAIR_DEVICE_TO_TV": "Koppel apparaten",
     "TV_NOT_FOUND": "TV niet gevonden. Heeft u de pincode correct ingevoerd?",
-    "AUTO_CAST_PAIR": "Automatisch koppelen",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "Automatisch koppelen vereist verbinding met Google-servers en werkt alleen met apparaten die door Chromecast worden ondersteund. Google zal geen gevoelige gegevens ontvangen, zoals uw foto's.",
+    "AUTO_CAST_PAIR": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "Koppelen met PIN",
     "CHOOSE_DEVICE_FROM_BROWSER": "Kies een compatibel apparaat uit de browser popup.",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "Koppelen met PIN werkt op elk groot schermapparaat waarop u uw album wilt afspelen.",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "Bezoek <a>{{url}}</a> op het apparaat dat je wilt koppelen.",
-    "CAST_AUTO_PAIR_FAILED": "Auto koppelen van Chromecast is mislukt. Probeer het opnieuw.",
+    "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "Losse hand",
     "APPLY_CROP": "Bijsnijden toepassen",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "Tenminste één transformatie of kleuraanpassing moet worden uitgevoerd voordat u opslaat.",

+ 4 - 4
web/packages/next/locales/pt-BR/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "Digite o código que você vê na TV abaixo para parear este dispositivo.",
     "PAIR_DEVICE_TO_TV": "Parear dispositivos",
     "TV_NOT_FOUND": "TV não encontrada. Você inseriu o PIN correto?",
-    "AUTO_CAST_PAIR": "Pareamento automático",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "O Auto Pair requer a conexão com servidores do Google e só funciona com dispositivos Chromecast. O Google não receberá dados confidenciais, como suas fotos.",
+    "AUTO_CAST_PAIR": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "Parear com PIN",
     "CHOOSE_DEVICE_FROM_BROWSER": "Escolha um dispositivo compatível com casts no navegador popup.",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "Parear com o PIN funciona para qualquer dispositivo de tela grande onde você deseja reproduzir seu álbum.",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "Acesse <a>{{url}}</a> no dispositivo que você deseja parear.",
-    "CAST_AUTO_PAIR_FAILED": "Chromecast Auto Pair falhou. Por favor, tente novamente.",
+    "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "Mão livre",
     "APPLY_CROP": "Aplicar Recorte",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "Pelo menos uma transformação ou ajuste de cor deve ser feito antes de salvar.",

+ 2 - 2
web/packages/next/locales/pt-PT/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 4 - 4
web/packages/next/locales/ru-RU/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "Введите код, который вы видите на экране телевизора ниже, чтобы выполнить сопряжение с этим устройством.",
     "PAIR_DEVICE_TO_TV": "Сопряжение устройств",
     "TV_NOT_FOUND": "Телевизор не найден. Вы правильно ввели PIN-код?",
-    "AUTO_CAST_PAIR": "Автоматическое сопряжение",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "Автоматическое сопряжение требует подключения к серверам Google и работает только с устройствами, поддерживающими Chromecast. Google не будет получать конфиденциальные данные, такие как ваши фотографии.",
+    "AUTO_CAST_PAIR": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "Соединение с помощью булавки",
     "CHOOSE_DEVICE_FROM_BROWSER": "Выберите устройство, совместимое с cast, во всплывающем окне браузера.",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "Сопряжение с помощью PIN-кода работает на любом устройстве с большим экраном, на котором вы хотите воспроизвести свой альбом.",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "Перейдите на страницу <a>{{url}}</a> на устройстве, которое вы хотите подключить.",
-    "CAST_AUTO_PAIR_FAILED": "Не удалось выполнить автоматическое сопряжение Chromecast. Пожалуйста, попробуйте снова.",
+    "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "От руки",
     "APPLY_CROP": "Применить обрезку",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "Перед сохранением необходимо выполнить по крайней мере одно преобразование или корректировку цвета.",

+ 2 - 2
web/packages/next/locales/sv-SE/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/th-TH/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 2 - 2
web/packages/next/locales/tr-TR/translation.json

@@ -599,10 +599,10 @@
     "PAIR_DEVICE_TO_TV": "",
     "TV_NOT_FOUND": "",
     "AUTO_CAST_PAIR": "",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "",
     "CHOOSE_DEVICE_FROM_BROWSER": "",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "",
     "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "",

+ 4 - 4
web/packages/next/locales/zh-CN/translation.json

@@ -598,13 +598,13 @@
     "ENTER_CAST_PIN_CODE": "输入您在下面的电视上看到的代码来配对此设备。",
     "PAIR_DEVICE_TO_TV": "配对设备",
     "TV_NOT_FOUND": "未找到电视。您输入的 PIN 码正确吗?",
-    "AUTO_CAST_PAIR": "自动配对",
-    "AUTO_CAST_PAIR_REQUIRES_CONNECTION_TO_GOOGLE": "自动配对需要连接到 Google 服务器,且仅适用于支持 Chromecast 的设备。Google 不会接收敏感数据,例如您的照片。",
+    "AUTO_CAST_PAIR": "",
+    "AUTO_CAST_PAIR_DESC": "",
     "PAIR_WITH_PIN": "用 PIN 配对",
     "CHOOSE_DEVICE_FROM_BROWSER": "从浏览器弹出窗口中选择兼容 Cast 的设备。",
-    "PAIR_WITH_PIN_WORKS_FOR_ANY_LARGE_SCREEN_DEVICE": "用 PIN 配对适用于任何大屏幕设备,您可以在这些设备上播放您的相册。",
+    "PAIR_WITH_PIN_DESC": "",
     "VISIT_CAST_ENTE_IO": "在您要配对的设备上访问 <a>{{url}}</a> 。",
-    "CAST_AUTO_PAIR_FAILED": "Chromecast 自动配对失败。请再试一次。",
+    "CAST_AUTO_PAIR_FAILED": "",
     "FREEHAND": "手画",
     "APPLY_CROP": "应用裁剪",
     "PHOTO_EDIT_REQUIRED_TO_SAVE": "保存之前必须至少执行一项转换或颜色调整。",

Некоторые файлы не были показаны из-за большого количества измененных файлов