Compare commits

...

8 Commits

Author SHA1 Message Date
Kingkor Roy Tirtho
25bafe1756 website: fix invalid nightly url 2026-02-20 22:33:00 +06:00
Kingkor Roy Tirtho
e135985308 chore: remove package assets folder 2026-02-20 20:29:48 +06:00
Kingkor Roy Tirtho
0ac949dc1b fix(android): build not working due to 2026-02-12 10:57:36 +06:00
Kingkor Roy Tirtho
e132c4036c chore: upgrade pubspec 2026-02-02 10:11:55 +06:00
Kingkor Roy Tirtho
759bf36387
cd: disable free disk space step 2026-02-01 22:16:18 +06:00
Kingkor Roy Tirtho
8bce0fa17d
fix: upgrade NewPipeExtractor to latest version fixinng playback issue 2026-02-01 17:28:00 +06:00
Tobse
b254ab6fe2
chore: replace windows app icon by a higher resolution (#2838)
Co-authored-by: Kingkor Roy Tirtho <krtirtho@gmail.com>
2025-11-17 09:51:06 +06:00
Kingkor Roy Tirtho
4838656dcc chore: fix alternative sources not persisting 2025-11-14 16:17:37 +06:00
25 changed files with 111 additions and 94 deletions

View File

@ -78,14 +78,14 @@ jobs:
cache: true cache: true
git-source: https://github.com/flutter/flutter.git git-source: https://github.com/flutter/flutter.git
- name: free disk space # - name: free disk space
if: ${{ matrix.platform == 'android' }} # if: ${{ matrix.platform == 'android' }}
run: | # run: |
sudo swapoff -a # sudo swapoff -a
sudo rm -f /swapfile # sudo rm -f /swapfile
sudo apt clean # sudo apt clean
docker rmi $(docker image ls -aq) # docker rmi $(docker image ls -aq)
df -h # df -h
- name: Setup Java - name: Setup Java
if: ${{matrix.platform == 'android'}} if: ${{matrix.platform == 'android'}}
uses: actions/setup-java@v4 uses: actions/setup-java@v4

View File

@ -27,6 +27,7 @@
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; } -keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.mozilla.javascript.** { *; } -keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter -keep class org.mozilla.classfile.ClassFileWriter
-dontwarn com.google.re2j.**
-dontwarn org.mozilla.javascript.tools.** -dontwarn org.mozilla.javascript.tools.**
-dontwarn javax.script.AbstractScriptEngine -dontwarn javax.script.AbstractScriptEngine

View File

@ -28,7 +28,7 @@ class HomeNewReleasesSection extends HookConsumerWidget {
if (newReleases.error if (newReleases.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _, message: _,
)) { )) {
return const SizedBox.shrink(); return const SizedBox.shrink();

View File

@ -48,7 +48,7 @@ class HomePageBrowseSection extends HookConsumerWidget {
if (browseSections.error if (browseSections.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _, message: _,
)) { )) {
return const SliverFillRemaining( return const SliverFillRemaining(

View File

@ -55,7 +55,7 @@ class UserAlbumsPage extends HookConsumerWidget {
if (albumsQuery.error if (albumsQuery.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _, message: _,
)) { )) {
return const Center(child: NoDefaultMetadataPlugin()); return const Center(child: NoDefaultMetadataPlugin());

View File

@ -60,7 +60,7 @@ class UserArtistsPage extends HookConsumerWidget {
if (artistQuery.error if (artistQuery.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _, message: _,
)) { )) {
return const Center(child: NoDefaultMetadataPlugin()); return const Center(child: NoDefaultMetadataPlugin());

View File

@ -83,7 +83,7 @@ class UserPlaylistsPage extends HookConsumerWidget {
if (playlistsQuery.error if (playlistsQuery.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _, message: _,
)) { )) {
return const Center(child: NoDefaultMetadataPlugin()); return const Center(child: NoDefaultMetadataPlugin());

View File

@ -85,7 +85,7 @@ class SearchPage extends HookConsumerWidget {
child: Builder(builder: (context) { child: Builder(builder: (context) {
if (searchChipSnapshot.error if (searchChipSnapshot.error
case MetadataPluginException( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
message: _ message: _
)) { )) {
return const NoDefaultMetadataPlugin(); return const NoDefaultMetadataPlugin();

View File

@ -144,40 +144,8 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
}), }),
audioPlayer.playlistStream.listen((playlist) async { audioPlayer.playlistStream.listen((playlist) async {
try { try {
// Playlist and state has to be in sync. This is only meant for final tracks =
// the shuffle/re-ordering indices to be in sync playlist.medias.map((e) => SpotubeMedia.media(e).track).toList();
if (playlist.medias.length != state.tracks.length) {
AppLogger.log.w(
"Playlist length does not match state tracks length. Ignoring... "
"Playlist length: ${playlist.medias.length}, "
"State tracks length: ${state.tracks.length}",
);
return;
}
final trackGroupedById = groupBy(
state.tracks,
(query) => query.id,
);
final tracks = <SpotubeTrackObject>[];
for (final media in playlist.medias) {
final track = trackGroupedById[SpotubeMedia.media(media).track.id]
?.firstOrNull;
if (track != null) {
tracks.add(track);
}
}
if (tracks.length != state.tracks.length) {
AppLogger.log.w("Mismatch in tracks after reordering/shuffling.");
final missingTracks =
state.tracks.where((track) => !tracks.contains(track)).toList();
AppLogger.log.w(
"Missing tracks: ${missingTracks.map((e) => e.id).join(", ")}",
);
}
state = state.copyWith( state = state.copyWith(
tracks: tracks, tracks: tracks,
@ -434,13 +402,31 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
return; return;
} }
final currentIndex = state.currentIndex; final oldState = state;
final currentTrack = state.activeTrack as SpotubeFullTrackObject; await audioPlayer.stop();
final swappedMedia = SpotubeMedia(currentTrack);
await audioPlayer.addTrackAt(swappedMedia, currentIndex + 1); await load(
await audioPlayer.skipToNext(); oldState.tracks,
await audioPlayer.removeTrack(currentIndex); initialIndex: oldState.currentIndex,
autoPlay: true,
);
state = state.copyWith(
collections: oldState.collections,
loopMode: oldState.loopMode,
playing: oldState.playing,
shuffled: false,
);
await audioPlayer.setLoopMode(oldState.loopMode);
await _updatePlayerState(
AudioPlayerStateTableCompanion(
tracks: Value(state.tracks),
currentIndex: Value(state.currentIndex),
collections: Value(state.collections),
loopMode: Value(state.loopMode),
playing: Value(state.playing),
shuffled: Value(state.shuffled),
),
);
} }
Future<void> jumpToTrack(SpotubeTrackObject track) async { Future<void> jumpToTrack(SpotubeTrackObject track) async {

View File

@ -12,7 +12,7 @@ final metadataPluginAlbumProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin.album.getAlbum(id); return metadataPlugin.album.getAlbum(id);

View File

@ -12,7 +12,7 @@ final metadataPluginArtistProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin.artist.getArtist(artistId); return metadataPlugin.artist.getArtist(artistId);

View File

@ -6,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:spotube/services/metadata/metadata.dart'; import 'package:spotube/services/metadata/metadata.dart';
part 'quality_presets.g.dart'; part 'quality_presets.g.dart';
@ -61,7 +62,7 @@ class AudioSourceAvailableQualityPresetsNotifier
audioSourceConfigSnapshot.whenData((audioSourceConfig) { audioSourceConfigSnapshot.whenData((audioSourceConfig) {
audioSourceSnapshot.whenData((audioSource) async { audioSourceSnapshot.whenData((audioSource) async {
if (audioSource == null || audioSourceConfig == null) { if (audioSource == null || audioSourceConfig == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
final preferences = await SharedPreferences.getInstance(); final preferences = await SharedPreferences.getInstance();
final persistedStateStr = final persistedStateStr =
@ -114,7 +115,7 @@ class AudioSourceAvailableQualityPresetsNotifier
final audioSourceConfig = await ref.read(metadataPluginsProvider final audioSourceConfig = await ref.read(metadataPluginsProvider
.selectAsync((data) => data.defaultAudioSourcePluginConfig)); .selectAsync((data) => data.defaultAudioSourcePluginConfig));
if (audioSourceConfig == null) { if (audioSourceConfig == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
final preferences = await SharedPreferences.getInstance(); final preferences = await SharedPreferences.getInstance();

View File

@ -131,7 +131,7 @@ final metadataPluginIsSavedPlaylistProvider =
final plugin = await ref.watch(metadataPluginProvider.future); final plugin = await ref.watch(metadataPluginProvider.future);
if (plugin == null) { if (plugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
final savedPlaylists = final savedPlaylists =

View File

@ -13,7 +13,7 @@ class MetadataPluginPlaylistNotifier
final metadataPlugin = await ref.read(metadataPluginProvider.future); final metadataPlugin = await ref.read(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin; return metadataPlugin;

View File

@ -9,7 +9,7 @@ final metadataPluginSearchAllProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin.search.all(query); return metadataPlugin.search.all(query);
@ -20,7 +20,7 @@ final metadataPluginSearchChipsProvider = FutureProvider((ref) async {
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin.search.chips; return metadataPlugin.search.chips;
}); });

View File

@ -8,7 +8,7 @@ final metadataPluginTrackProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return metadataPlugin.track.getTrack(trackId); return metadataPlugin.track.getTrack(trackId);

View File

@ -20,7 +20,7 @@ mixin MetadataPluginMixin<K>
final plugin = await ref.read(metadataPluginProvider.future); final plugin = await ref.read(metadataPluginProvider.future);
if (plugin == null) { if (plugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
return plugin; return plugin;

View File

@ -95,7 +95,7 @@ class TrackOptionsActions {
final metadataPlugin = await ref.read(metadataPluginProvider.future); final metadataPlugin = await ref.read(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin(); throw MetadataPluginException.noDefaultMetadataPlugin();
} }
final tracks = await metadataPlugin.track.radio(track.id); final tracks = await metadataPlugin.track.radio(track.id);

View File

@ -9,7 +9,8 @@ enum MetadataPluginErrorCode {
pluginDownloadFailed, pluginDownloadFailed,
duplicatePlugin, duplicatePlugin,
pluginByteCodeFileNotFound, pluginByteCodeFileNotFound,
noDefaultPlugin, noDefaultMetadataPlugin,
noDefaultAudiSourcePlugin,
} }
class MetadataPluginException implements Exception { class MetadataPluginException implements Exception {
@ -68,10 +69,15 @@ class MetadataPluginException implements Exception {
'Plugin byte code file, plugin.out not found. Please ensure the plugin is correctly packaged.', 'Plugin byte code file, plugin.out not found. Please ensure the plugin is correctly packaged.',
errorCode: MetadataPluginErrorCode.pluginByteCodeFileNotFound, errorCode: MetadataPluginErrorCode.pluginByteCodeFileNotFound,
); );
MetadataPluginException.noDefaultPlugin() MetadataPluginException.noDefaultMetadataPlugin()
: this._( : this._(
'No default metadata plugin is set. Please set a default plugin in the settings.', 'No default metadata plugin is set. Please set a default plugin in the settings.',
errorCode: MetadataPluginErrorCode.noDefaultPlugin, errorCode: MetadataPluginErrorCode.noDefaultMetadataPlugin,
);
MetadataPluginException.noDefaultAudioSourcePlugin()
: this._(
'No default audio source plugin is set. Please set a default plugin in the settings.',
errorCode: MetadataPluginErrorCode.noDefaultAudiSourcePlugin,
); );
@override @override

View File

@ -12,6 +12,7 @@ import 'package:spotube/provider/metadata_plugin/audio_source/quality_presets.da
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/dio/dio.dart'; import 'package:spotube/services/dio/dio.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:spotube/services/sourced_track/exceptions.dart'; import 'package:spotube/services/sourced_track/exceptions.dart';
import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/service_utils.dart';
@ -41,7 +42,7 @@ class SourcedTrack extends BasicSourcedTrack {
final audioSourceConfig = await ref.read(metadataPluginsProvider final audioSourceConfig = await ref.read(metadataPluginsProvider
.selectAsync((data) => data.defaultAudioSourcePluginConfig)); .selectAsync((data) => data.defaultAudioSourcePluginConfig));
if (audioSource == null || audioSourceConfig == null) { if (audioSource == null || audioSourceConfig == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
final database = ref.read(databaseProvider); final database = ref.read(databaseProvider);
@ -157,7 +158,7 @@ class SourcedTrack extends BasicSourcedTrack {
final audioSource = await ref.read(audioSourcePluginProvider.future); final audioSource = await ref.read(audioSourcePluginProvider.future);
if (audioSource == null) { if (audioSource == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
final videoResults = <SpotubeAudioSourceMatchObject>[]; final videoResults = <SpotubeAudioSourceMatchObject>[];
@ -190,7 +191,8 @@ class SourcedTrack extends BasicSourcedTrack {
} }
Future<SourcedTrack?> swapWithSibling( Future<SourcedTrack?> swapWithSibling(
SpotubeAudioSourceMatchObject sibling) async { SpotubeAudioSourceMatchObject sibling,
) async {
if (sibling.id == info.id) { if (sibling.id == info.id) {
return null; return null;
} }
@ -199,7 +201,7 @@ class SourcedTrack extends BasicSourcedTrack {
final audioSourceConfig = await ref.read(metadataPluginsProvider final audioSourceConfig = await ref.read(metadataPluginsProvider
.selectAsync((data) => data.defaultAudioSourcePluginConfig)); .selectAsync((data) => data.defaultAudioSourcePluginConfig));
if (audioSource == null || audioSourceConfig == null) { if (audioSource == null || audioSourceConfig == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
// a sibling source that was fetched from the search results // a sibling source that was fetched from the search results
@ -216,10 +218,19 @@ class SourcedTrack extends BasicSourcedTrack {
final database = ref.read(databaseProvider); final database = ref.read(databaseProvider);
// Delete the old Entry
await (database.sourceMatchTable.delete()
..where(
(table) =>
table.trackId.equals(query.id) &
table.sourceType.equals(audioSourceConfig.slug),
))
.go();
await database.into(database.sourceMatchTable).insert( await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert( SourceMatchTableCompanion.insert(
trackId: query.id, trackId: query.id,
sourceInfo: Value(jsonEncode(siblings.first)), sourceInfo: Value(jsonEncode(sibling)),
sourceType: audioSourceConfig.slug, sourceType: audioSourceConfig.slug,
createdAt: Value(DateTime.now()), createdAt: Value(DateTime.now()),
), ),
@ -245,7 +256,7 @@ class SourcedTrack extends BasicSourcedTrack {
final audioSourceConfig = await ref.read(metadataPluginsProvider final audioSourceConfig = await ref.read(metadataPluginsProvider
.selectAsync((data) => data.defaultAudioSourcePluginConfig)); .selectAsync((data) => data.defaultAudioSourcePluginConfig));
if (audioSource == null || audioSourceConfig == null) { if (audioSource == null || audioSourceConfig == null) {
throw Exception("Dude wat?"); throw MetadataPluginException.noDefaultAudioSourcePlugin();
} }
List<SpotubeAudioSourceStreamObject> validStreams = []; List<SpotubeAudioSourceStreamObject> validStreams = [];

View File

@ -745,11 +745,11 @@ packages:
dependency: transitive dependency: transitive
description: description:
path: "." path: "."
ref: master ref: HEAD
resolved-ref: "922f9f9eafd8b501da83dca67d56b2887fa8f916" resolved-ref: "458046cd9a88924e5074d96ba45397219d53b230"
url: "https://github.com/TiffApps/fk_user_agent.git" url: "https://github.com/maeltoukap/fk_user_agent.git"
source: git source: git
version: "2.1.1" version: "2.1.0"
fluentui_system_icons: fluentui_system_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -947,8 +947,8 @@ packages:
description: description:
path: "." path: "."
ref: HEAD ref: HEAD
resolved-ref: "898fd4ebcef77f5177b08aa6f9b9047bd02c6b9b" resolved-ref: ab3ff415114b7b43593e6ee718ad3d760af18350
url: "https://github.com/KRTirtho/flutter_new_pipe_extractor.git" url: "https://github.com/KRTirtho/flutter_new_pipe_extractor"
source: git source: git
version: "0.1.0" version: "0.1.0"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
@ -1189,7 +1189,7 @@ packages:
description: description:
path: "." path: "."
ref: main ref: main
resolved-ref: "32828156bc111d147709f8d644804227bbdfe8f1" resolved-ref: d85dd429241d464a8b5b0c2b3d870143eeba8b46
url: "https://github.com/KRTirtho/hetu_spotube_plugin.git" url: "https://github.com/KRTirtho/hetu_spotube_plugin.git"
source: git source: git
version: "0.0.2" version: "0.0.2"
@ -2728,10 +2728,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: youtube_explode_dart name: youtube_explode_dart
sha256: add33de45d80c7f71a5e3dd464dd82fafd7fb5ab875fd303c023f30f76618325 sha256: "3d731d71df9901b1915bae806781df519cff32517e36db279f844ae619669e45"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.5"
yt_dlp_dart: yt_dlp_dart:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -114,14 +114,14 @@ dependencies:
wikipedia_api: ^0.1.0 wikipedia_api: ^0.1.0
win32_registry: ^1.1.5 win32_registry: ^1.1.5
window_manager: ^0.4.3 window_manager: ^0.4.3
youtube_explode_dart: ^3.0.0 youtube_explode_dart: ^3.0.5
yt_dlp_dart: yt_dlp_dart:
git: git:
url: https://github.com/KRTirtho/yt_dlp_dart.git url: https://github.com/KRTirtho/yt_dlp_dart.git
ref: 4e5310e14af74bdbb51e2a4766e66d6c6a2562a8 ref: 4e5310e14af74bdbb51e2a4766e66d6c6a2562a8
flutter_new_pipe_extractor: flutter_new_pipe_extractor:
git: git:
url: https://github.com/KRTirtho/flutter_new_pipe_extractor.git url: https://github.com/KRTirtho/flutter_new_pipe_extractor
http_parser: ^4.1.2 http_parser: ^4.1.2
collection: any collection: any
archive: ^4.0.7 archive: ^4.0.7
@ -239,6 +239,8 @@ flutter:
- packages/hetu_std/assets/bytecode/std.out - packages/hetu_std/assets/bytecode/std.out
- packages/hetu_otp_util/assets/bytecode/otp_util.out - packages/hetu_otp_util/assets/bytecode/otp_util.out
- packages/hetu_spotube_plugin/assets/bytecode/spotube_plugin.out - packages/hetu_spotube_plugin/assets/bytecode/spotube_plugin.out
# NewPipe binaries (desktop only)
# - packages/flutter_new_pipe_extractor/assets/
fonts: fonts:
- family: RadixIcons - family: RadixIcons
fonts: fonts:

View File

@ -22,6 +22,8 @@ export const routes: Record<string, [string, IconType | null]> = {
const releasesUrl = const releasesUrl =
"https://github.com/KRTirtho/Spotube/releases/latest/download"; "https://github.com/KRTirtho/Spotube/releases/latest/download";
const releasesNightlyUrl =
"https://github.com/KRTirtho/Spotube/releases/download/nightly";
export const downloadLinks: Record<string, [string, IconType[]]> = { export const downloadLinks: Record<string, [string, IconType[]]> = {
"Android Apk": [`${releasesUrl}/Spotube-android-all-arch.apk`, [FaAndroid]], "Android Apk": [`${releasesUrl}/Spotube-android-all-arch.apk`, [FaAndroid]],
@ -87,39 +89,47 @@ export const extendedNightlyDownloadLinks: Record<
string, string,
[string, IconType[], string] [string, IconType[], string]
> = { > = {
Android: [`${releasesUrl}/Spotube-android-all-arch.apk`, [FaAndroid], "apk"], Android: [
`${releasesNightlyUrl}/Spotube-android-all-arch.apk`,
[FaAndroid],
"apk",
],
Windows: [ Windows: [
`${releasesUrl}/Spotube-windows-x86_64-setup.exe`, `${releasesNightlyUrl}/Spotube-windows-x86_64-setup.exe`,
[FaWindows], [FaWindows],
"exe", "exe",
], ],
macOS: [`${releasesUrl}/Spotube-macos-universal.dmg`, [FaApple], "dmg"], macOS: [
`${releasesNightlyUrl}/Spotube-macos-universal.dmg`,
[FaApple],
"dmg",
],
"Ubuntu, Debian (x64)": [ "Ubuntu, Debian (x64)": [
`${releasesUrl}/Spotube-linux-x86_64.deb`, `${releasesNightlyUrl}/Spotube-linux-x86_64.deb`,
[FaUbuntu, FaDebian], [FaUbuntu, FaDebian],
"deb", "deb",
], ],
"Ubuntu, Debian (arm64)": [ "Ubuntu, Debian (arm64)": [
`${releasesUrl}/Spotube-linux-aarch64.deb`, `${releasesNightlyUrl}/Spotube-linux-aarch64.deb`,
[FaUbuntu, FaDebian], [FaUbuntu, FaDebian],
"deb", "deb",
], ],
"Fedora, Redhat, Opensuse": [ "Fedora, Redhat, Opensuse": [
`${releasesUrl}/Spotube-linux-x86_64.rpm`, `${releasesNightlyUrl}/Spotube-linux-x86_64.rpm`,
[FaFedora, FaRedhat, FaOpensuse], [FaFedora, FaRedhat, FaOpensuse],
"rpm", "rpm",
], ],
"Linux AppImage (x64)": [ "Linux AppImage (x64)": [
`${releasesUrl}/Spotube-linux-x86_64.AppImage`, `${releasesNightlyUrl}/Spotube-linux-x86_64.AppImage`,
[FaLinux], [FaLinux],
"AppImage", "AppImage",
], ],
"Linux AppImage (arm64)": [ "Linux AppImage (arm64)": [
`${releasesUrl}/Spotube-linux-aarch64.AppImage`, `${releasesNightlyUrl}/Spotube-linux-aarch64.AppImage`,
[FaLinux], [FaLinux],
"AppImage", "AppImage",
], ],
iPhone: [`${releasesUrl}/Spotube-iOS.ipa`, [FaApple], "ipa"], iPhone: [`${releasesNightlyUrl}/Spotube-iOS.ipa`, [FaApple], "ipa"],
}; };
export const ADS_SLOTS = Object.freeze({ export const ADS_SLOTS = Object.freeze({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 202 KiB