fix: windows SSL Certificate error breaking login #905 (#1474)

* fix: certificate error by using custom ssl certificate

* Cd/docker linux ar (#1468)

* cd: use docker buildx

* cd: use linux host for linux arm instead of macos m1

m1 doesn't support nested virtualization. (Apple truly sucks)

* cd: don't specify arch in Dockerfile

* cd: use custom Dockerfile from ubuntu instead of flutter image

* cd: add setup java for android

* cd: add flutter distributor pre-built docker image for arm

* cd: save me from this cursed arm build

* cd: ??

* cd: ??

* cd: use docker build

* fix: windows SSL Exception for Signing in

* refactor: extract update checker as a basic function instead of a hook
This commit is contained in:
Kingkor Roy Tirtho 2024-05-04 20:10:19 +06:00 committed by GitHub
parent 88fea7ecf9
commit 937a706ac9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 218 additions and 157 deletions

View File

@ -2,3 +2,5 @@ build
dist
.dart_tool
.idea
.github
.git

23
.github/Dockerfile vendored
View File

@ -1,32 +1,27 @@
ARG FLUTTER_VERSION
ARG BUILD_VERSION
FROM --platform=arm64 fischerscode/flutter-sudo:${FLUTTER_VERSION}
FROM --platform=linux/arm64 krtirtho/flutter_distributor_arm64:${FLUTTER_VERSION}
ARG BUILD_VERSION
WORKDIR /app
# Install dependencies
RUN sudo apt-get update &&\
sudo apt-get install -y tar clang cmake ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev rpm &&\
sudo rm -rf /var/lib/apt/lists/*
COPY . .
RUN sudo chown -R $(whoami) /app
RUN chown -R $(whoami) /app
RUN flutter pub get &&\
flutter config --enable-linux-desktop &&\
flutter pub get &&\
dart run build_runner build --delete-conflicting-outputs
RUN dart pub global activate flutter_distributor &&\
alias dpkg-deb="dpkg-deb --Zxz" &&\
flutter_distributor package --platform=linux --targets=deb &&\
flutter_distributor package --platform=linux --targets=rpm
RUN alias dpkg-deb="dpkg-deb --Zxz" &&\
flutter_distributor package --platform=linux --targets=deb
RUN make tar VERSION=${BUILD_VERSION} ARCH=arm64 PKG_ARCH=aarch64
RUN mv build/spotube-linux-*-aarch64.tar.xz dist/ &&\
mv dist/**/spotube-*-linux.deb dist/Spotube-linux-aarch64.deb &&\
mv dist/**/spotube-*-linux.rpm dist/Spotube-linux-aarch64.rpm
mv dist/**/spotube-*-linux.deb dist/Spotube-linux-aarch64.deb
CMD [ "sleep", "5000000" ]

23
.github/Dockerfile.flutter_distributor vendored Normal file
View File

@ -0,0 +1,23 @@
FROM --platform=linux/arm64 ubuntu:22.04
ARG FLUTTER_VERSION
RUN apt-get clean &&\
apt-get update &&\
apt-get install -y bash curl file git unzip xz-utils zip libglu1-mesa cmake tar clang ninja-build pkg-config libgtk-3-dev make python3-pip python3-setuptools desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace fuse libunwind-dev locate patchelf gir1.2-appindicator3-0.1 libappindicator3-1 libappindicator3-dev libsecret-1-0 libjsoncpp25 libsecret-1-dev libjsoncpp-dev libnotify-bin libnotify-dev mpv libmpv-dev rpm && \
rm -rf /var/lib/apt/lists/*
WORKDIR /home/flutter
RUN git clone https://github.com/flutter/flutter.git -b ${FLUTTER_VERSION} --single-branch flutter-sdk
RUN flutter-sdk/bin/flutter precache
RUN flutter-sdk/bin/flutter config --no-analytics
ENV PATH="$PATH:/home/flutter/flutter-sdk/bin"
ENV PATH="$PATH:/home/flutter/flutter-sdk/bin/cache/dart-sdk/bin"
ENV PATH="$PATH:/home/flutter/.pub-cache/bin"
ENV PUB_CACHE="/home/flutter/.pub-cache"
RUN dart pub global activate flutter_distributor

View File

@ -207,34 +207,35 @@ jobs:
limit-access-to-actor: true
linux_arm:
runs-on: macos-14
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Docker
run: brew install --cask docker
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
if: ${{ inputs.channel == 'nightly' }}
run: |
brew install yq
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
- name: BUILD_VERSION Env (stable)
if: ${{ inputs.channel == 'stable' }}
run: |
echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
- name: Replace Version in files
- name: Install Dependencies
run: |
sed -i '' 's|%{{APPDATA_RELEASE}}%|<release version="${{ env.BUILD_VERSION }}" date="${{ steps.date.outputs.date }}" />|' linux/com.github.KRTirtho.Spotube.appdata.xml
sudo apt-get update -y
sudo apt-get install -y pkg-config make python3-pip python3-setuptools
- name: Replace pubspec version and BUILD_VERSION Env (nightly)
if: ${{ inputs.channel == 'nightly' }}
run: |
curl -sS https://webi.sh/yq | sh
yq -i '.version |= sub("\+\d+", "+${{ inputs.channel }}.")' pubspec.yaml
yq -i '.version += strenv(GITHUB_RUN_NUMBER)' pubspec.yaml
echo "BUILD_VERSION=${{ inputs.version }}+${{ inputs.channel }}.${{ github.run_number }}" >> $GITHUB_ENV
- name: BUILD_VERSION Env (stable)
if: ${{ inputs.channel == 'stable' }}
run: |
echo "BUILD_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
- name: Create Stable .env
if: ${{ inputs.channel == 'stable' }}
@ -244,20 +245,42 @@ jobs:
if: ${{ inputs.channel == 'nightly' }}
run: echo '${{ secrets.DOTENV_NIGHTLY }}' > .env
- name: Build Linux Arm
- name: Replace Version in files
run: |
docker build -t spotube-linux-arm -f .github/Dockerfile . --build-arg BUILD_VERSION=${{ env.BUILD_VERSION }} --build-arg FLUTTER_VERSION=${{ env.FLUTTER_VERSION }}
docker create --name spotube-linux-arm spotube-linux-arm
docker cp spotube-linux-arm:/app/dist .
docker rm -f spotube-linux-arm
sed -i 's|%{{APPDATA_RELEASE}}%|<release version="${{ env.BUILD_VERSION }}" date="${{ steps.date.outputs.date }}" />|' linux/com.github.KRTirtho.Spotube.appdata.xml
- name: Build Binaries (stable)
if: ${{ inputs.channel == 'stable' }}
run: |
docker buildx build --platform=linux/arm64 -f .github/Dockerfile . --build-arg FLUTTER_VERSION=${{env.FLUTTER_VERSION}} --build-arg BUILD_VERSION=${{env.BUILD_VERSION}} -t krtirtho/spotube_linux_arm:latest --load
- name: Build Binaries (nightly)
if: ${{ inputs.channel == 'nightly' }}
run: |
docker buildx build --platform=linux/arm64 -f .github/Dockerfile . --build-arg FLUTTER_VERSION=${{env.FLUTTER_VERSION}} --build-arg BUILD_VERSION=nightly -t krtirtho/spotube_linux_arm:latest --load
- name: Copy the built packages
run: |
docker images ls
docker create --name spotube_linux_arm krtirtho/spotube_linux_arm:latest
docker cp spotube_linux_arm:/app/dist/ dist/
- uses: actions/upload-artifact@v3
if: ${{ inputs.channel == 'stable' }}
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
dist/Spotube-linux-aarch64.deb
dist/spotube-linux-${{ env.BUILD_VERSION }}-aarch64.tar.xz
- uses: actions/upload-artifact@v3
if: ${{ inputs.channel == 'nightly' }}
with:
if-no-files-found: error
name: Spotube-Release-Binaries
path: |
dist/Spotube-linux-aarch64.deb
dist/Spotube-linux-aarch64.rpm
dist/spotube-linux-nightly-aarch64.tar.xz
- name: Debug With SSH When fails
@ -266,7 +289,6 @@ jobs:
with:
limit-access-to-actor: true
android:
runs-on: ubuntu-latest
steps:
@ -275,6 +297,13 @@ jobs:
with:
cache: true
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
cache: 'gradle'
check-latest: true
- name: Install Dependencies
run: |

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:spotube/components/shared/links/anchor_button.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:version/version.dart';
class RootAppUpdateDialog extends StatelessWidget {
final Version? version;
const RootAppUpdateDialog({super.key, this.version});
@override
Widget build(BuildContext context) {
const url = "https://spotube.krtirtho.dev/downloads";
return AlertDialog(
title: const Text("Spotube has an update"),
actions: [
FilledButton(
child: const Text("Download Now"),
onPressed: () => launchUrlString(
url,
mode: LaunchMode.externalApplication,
),
),
],
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Spotube v$version has been released"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Read the latest "),
AnchorButton(
"release notes",
style: const TextStyle(color: Colors.blue),
onTap: () => launchUrlString(
url,
mode: LaunchMode.externalApplication,
),
),
],
),
],
),
);
}
}

View File

@ -1,100 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:spotube/collections/env.dart';
import 'package:spotube/components/shared/links/anchor_button.dart';
import 'package:spotube/hooks/controllers/use_package_info.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:version/version.dart';
void useUpdateChecker(WidgetRef ref) {
final isCheckUpdateEnabled =
ref.watch(userPreferencesProvider.select((s) => s.checkUpdate));
final packageInfo = usePackageInfo(
appName: 'Spotube',
packageName: 'spotube',
);
final Future<List<Version?>> Function() checkUpdate = useCallback(
() async {
final value = await http.get(
Uri.parse(
"https://api.github.com/repos/KRTirtho/spotube/releases/latest"),
);
final tagName =
(jsonDecode(value.body)["tag_name"] as String).replaceAll("v", "");
final currentVersion = packageInfo.version == "Unknown"
? null
: Version.parse(packageInfo.version);
final latestVersion =
tagName == "nightly" ? null : Version.parse(tagName);
return [currentVersion, latestVersion];
},
[packageInfo.version],
);
final context = useContext();
download(String url) => launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
useEffect(() {
if (!Env.enableUpdateChecker) return;
if (!isCheckUpdateEnabled) return null;
checkUpdate().then((value) {
final currentVersion = value.first;
final latestVersion = value.last;
if (currentVersion == null ||
latestVersion == null ||
(latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
if (latestVersion <= currentVersion) return;
showDialog(
context: context,
barrierDismissible: true,
barrierColor: Colors.black26,
builder: (context) {
const url =
"https://spotube.krtirtho.dev/downloads";
return AlertDialog(
title: const Text("Spotube has an update"),
actions: [
FilledButton(
child: const Text("Download Now"),
onPressed: () => download(url),
),
],
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("Spotube v${value.last} has been released"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Read the latest "),
AnchorButton(
"release notes",
style: const TextStyle(color: Colors.blue),
onTap: () => launchUrlString(
url,
mode: LaunchMode.externalApplication,
),
),
],
),
],
),
);
},
);
});
return null;
}, [packageInfo, isCheckUpdateEnabled]);
}

View File

@ -14,13 +14,13 @@ import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/components/root/spotube_navigation_bar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/configurators/use_endless_playback.dart';
import 'package:spotube/hooks/configurators/use_update_checker.dart';
import 'package:spotube/provider/connect/server.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/services/connectivity_adapter.dart';
import 'package:spotube/utils/persisted_state_notifier.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/service_utils.dart';
const rootPaths = {
"/": 0,
@ -46,6 +46,8 @@ class RootApp extends HookConsumerWidget {
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
ServiceUtils.checkForUpdates(context, ref);
final sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.getBool(kIsUsingEncryption) == false &&
@ -160,7 +162,6 @@ class RootApp extends HookConsumerWidget {
}, [downloader]);
// checks for latest version of the application
useUpdateChecker(ref);
useEndlessPlayback(ref);

View File

@ -1,10 +1,12 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'
hide X509Certificate;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/components/shared/dialogs/prompt_dialog.dart';
import 'package:spotube/extensions/context.dart';
@ -18,6 +20,18 @@ class AuthenticationCredentials {
bool get isExpired => DateTime.now().isAfter(expiration);
static final Dio dio = () {
final dio = Dio();
(dio.httpClientAdapter as IOHttpClientAdapter)
.createHttpClient = () => HttpClient()
..badCertificateCallback = (X509Certificate cert, String host, int port) {
return host.endsWith("spotify.com") && port == 443;
};
return dio;
}();
AuthenticationCredentials({
required this.cookie,
required this.accessToken,
@ -30,21 +44,23 @@ class AuthenticationCredentials {
.split("; ")
.firstWhereOrNull((c) => c.trim().startsWith("sp_dc="))
?.trim();
final res = await get(
final res = await dio.getUri(
Uri.parse(
"https://open.spotify.com/get_access_token?reason=transport&productType=web_player",
),
headers: {
"Cookie": spDc ?? "",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
},
options: Options(
headers: {
"Cookie": spDc ?? "",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
},
),
);
final body = jsonDecode(res.body);
final body = res.data;
if (res.statusCode >= 400) {
if ((res.statusCode ?? 500) >= 400) {
throw Exception(
"Failed to get access token: ${body['error'] ?? res.reasonPhrase}",
"Failed to get access token: ${body['error'] ?? res.statusMessage}",
);
}

View File

@ -1,10 +1,10 @@
import 'dart:convert';
import 'package:flutter/widgets.dart' hide Element;
import 'package:go_router/go_router.dart';
import 'package:html/dom.dart';
import 'package:html/dom.dart' hide Text;
import 'package:spotify/spotify.dart';
import 'package:spotube/components/library/user_local_tracks.dart';
import 'package:spotube/components/root/update_dialog.dart';
import 'package:spotube/models/logger.dart';
import 'package:http/http.dart' as http;
import 'package:spotube/models/lyrics.dart';
@ -14,6 +14,16 @@ import 'package:spotube/utils/primitive_utils.dart';
import 'package:collection/collection.dart';
import 'package:html/parser.dart' as parser;
import 'dart:async';
import 'package:flutter/material.dart' hide Element;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:version/version.dart';
abstract class ServiceUtils {
static final logger = getLogger("ServiceUtils");
@ -318,4 +328,42 @@ abstract class ServiceUtils {
}
});
}
static Future<void> checkForUpdates(
BuildContext context,
WidgetRef ref,
) async {
if (!Env.enableUpdateChecker) return;
if (!ref.read(userPreferencesProvider.select((s) => s.checkUpdate))) return;
final packageInfo = await PackageInfo.fromPlatform();
final value = await http.get(
Uri.parse(
"https://api.github.com/repos/KRTirtho/spotube/releases/latest",
),
);
final tagName =
(jsonDecode(value.body)["tag_name"] as String).replaceAll("v", "");
final currentVersion = packageInfo.version == "Unknown"
? null
: Version.parse(packageInfo.version);
final latestVersion = tagName == "nightly" ? null : Version.parse(tagName);
if (currentVersion == null ||
latestVersion == null ||
(latestVersion.isPreRelease && !currentVersion.isPreRelease) ||
(!latestVersion.isPreRelease && currentVersion.isPreRelease)) return;
if (latestVersion <= currentVersion || !context.mounted) return;
showDialog(
context: context,
barrierDismissible: true,
barrierColor: Colors.black26,
builder: (context) {
return RootAppUpdateDialog(version: latestVersion);
},
);
}
}

View File

@ -153,6 +153,7 @@ flutter:
uses-material-design: true
assets:
- assets/
- assets/ca/
- assets/tutorial/
- assets/logos/
- LICENSE