Project import generated by Copybara.

GitOrigin-RevId: d14f9c569b8c92f77b278db43796405a1b66b888
This commit is contained in:
Madari Developers 2025-01-08 06:12:05 +00:00
parent 680fdb4c78
commit a297699b6c
12 changed files with 234 additions and 107 deletions

View file

@ -124,7 +124,7 @@ jobs:
dart run build_runner build --delete-conflicting-outputs
- name: Build iOS
run: flutter build ios --release --no-codesign
run: make build_ipa
- name: Create and Pack IPA
run: |
@ -178,7 +178,7 @@ jobs:
dart run build_runner build --delete-conflicting-outputs
- name: Build Linux
run: flutter build linux --release
run: make build_linux
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
@ -222,7 +222,7 @@ jobs:
dart run build_runner build --delete-conflicting-outputs
- name: Build MacOS
run: flutter build macos --release
run: make build_mac
- name: Isolate the Build
run: mkdir build/macos/Build/Products/Release/AppRelease

View file

@ -1,5 +1,7 @@
.PHONY: build schema build_web build_mac build_android build_windows build_ipa build_linux
BUILD_ID := $(or $(GITHUB_RUN_ID),dev)
.PHONY: build schema build_web build_mac
build:
dart run build_runner build --delete-conflicting-outputs
@ -7,13 +9,19 @@ schema:
dart run drift_dev schema dump lib/database/database.dart drift_schemas/drift_schema_v1.json
build_web:
flutter build web --target lib/main_web.dart --release --pwa-strategy none --wasm
flutter build web --target lib/main_web.dart --release --pwa-strategy none --wasm --dart-define=BUILD_ID=$(BUILD_ID)
build_mac:
flutter build macos --target lib/main.dart --release
flutter build macos --target lib/main.dart --release --dart-define=BUILD_ID=$(BUILD_ID)
build_android:
flutter build apk --release
flutter build apk --release --dart-define=BUILD_ID=$(BUILD_ID)
build_windows:
flutter build windows --release
flutter build windows --release --dart-define=BUILD_ID=$(BUILD_ID)
build_ipa:
flutter build ios --release --no-codesign --dart-define=BUILD_ID=$(BUILD_ID)
build_linux:
flutter build linux --release --dart-define=BUILD_ID=$(BUILD_ID)

View file

@ -313,6 +313,8 @@ class Meta extends LibraryItem {
@JsonKey(includeFromJson: false, includeToJson: false)
final int? traktId;
final dynamic externalIds;
String get imdbRating {
return (imdbRating_ ?? "").toString();
}
@ -329,6 +331,7 @@ class Meta extends LibraryItem {
this.cast,
this.traktId,
this.country,
this.externalIds,
this.description,
this.genre,
this.imdbRating_,

View file

@ -38,7 +38,6 @@ class RenderStreamList extends StatefulWidget {
}
class _RenderStreamListState extends State<RenderStreamList> {
Stream<List<StreamList>>? _stream;
final Map<String, double> _downloadProgress = {};
final Map<String, String> _downloadError = {};

View file

@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:madari_client/features/connection/types/stremio.dart';
import 'package:madari_client/features/connections/service/base_connection_service.dart';
@ -50,11 +51,27 @@ class StremioCard extends StatelessWidget {
);
}
Video? get currentVideo {
return (item as Meta).videos?.firstWhere((episode) {
return (item as Meta).nextEpisode == episode.episode &&
(item as Meta).nextSeason == episode.season;
});
}
bool get isInFuture {
final video = currentVideo;
return video != null &&
video.firstAired != null &&
video.firstAired!.isAfter(DateTime.now());
}
_buildWideCard(BuildContext context, Meta meta) {
if (meta.background == null) {
return Container();
}
final video = currentVideo;
return Container(
decoration: BoxDecoration(
image: DecorationImage(
@ -67,6 +84,21 @@ class StremioCard extends StatelessWidget {
),
child: Stack(
children: [
if (isInFuture)
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black,
Colors.transparent,
],
begin: Alignment.topLeft,
end: Alignment.bottomCenter,
),
),
),
),
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
@ -90,6 +122,10 @@ class StremioCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("S${meta.nextSeason} E${meta.nextEpisode}"),
Text(
"${meta.name}",
style: Theme.of(context).textTheme.bodyLarge,
),
Text(
"${meta.nextEpisodeTitle}".trim(),
style: Theme.of(context).textTheme.headlineSmall,
@ -98,6 +134,38 @@ class StremioCard extends StatelessWidget {
),
),
),
if (isInFuture)
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
getRelativeDate(video!.firstAired!),
style: Theme.of(context).textTheme.bodyLarge,
),
),
if (isInFuture)
const Positioned(
bottom: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(
horizontal: 4,
vertical: 10,
),
child: Icon(
Icons.calendar_month,
),
),
],
),
),
),
const Positioned(
child: Center(
child: IconButton.filled(
@ -176,13 +244,21 @@ class StremioCard extends StatelessWidget {
return Hero(
tag: "$prefix${meta.type}${item.id}",
child: AspectRatio(
aspectRatio: 2 / 3,
child: (backgroundImage == null)
? Text("${meta.name}")
: Stack(
child: (backgroundImage == null)
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
Text(meta.name ?? "No name"),
if (meta.description != null) Text(meta.description!),
],
),
)
: Stack(
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: CachedNetworkImageProvider(
@ -230,72 +306,92 @@ class StremioCard extends StatelessWidget {
)
: const SizedBox.shrink(),
),
if (meta.progress != null)
const Positioned.fill(
child: IconButton(
onPressed: null,
icon: Icon(
Icons.play_arrow,
size: 24,
),
if (meta.progress != null)
const Positioned.fill(
child: IconButton(
onPressed: null,
icon: Icon(
Icons.play_arrow,
size: 24,
),
),
),
if (meta.progress != null)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(
value: meta.progress,
),
),
if (meta.nextEpisode != null && meta.nextSeason != null)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.grey,
Colors.transparent,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4, horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
meta.name ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(fontWeight: FontWeight.w600),
),
Text(
"S${meta.nextSeason} E${meta.nextEpisode}",
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
),
if (meta.progress != null)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(
value: meta.progress,
),
),
if (meta.nextEpisode != null && meta.nextSeason != null)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.grey,
Colors.transparent,
],
begin: Alignment.bottomLeft,
end: Alignment.topRight,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4, horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
meta.name ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(fontWeight: FontWeight.w600),
),
Text(
"S${meta.nextSeason} E${meta.nextEpisode}",
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
),
)
],
),
),
)
],
),
);
}
}
String getRelativeDate(DateTime date) {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final tomorrow = DateTime(now.year, now.month, now.day + 1);
final difference = date.difference(today).inDays;
if (date.isAtSameMomentAs(today)) {
return "It's today!";
} else if (date.isAtSameMomentAs(tomorrow)) {
return "Coming up tomorrow!";
} else if (difference > 1 && difference < 7) {
return "Coming up in $difference days";
} else if (difference >= 7 && difference < 14) {
return "Coming up next ${DateFormat('EEEE').format(date)}";
} else {
return "On ${DateFormat('MM/dd/yyyy').format(date)}";
}
}

View file

@ -0,0 +1,13 @@
import 'package:media_kit/media_kit.dart';
class TraktIntegrationVideo {
Player player;
TraktIntegrationVideo({
required this.player,
});
initState() {}
dispose() {}
}

View file

@ -28,7 +28,7 @@ class _TraktContainerState extends State<TraktContainer> {
key: widget.loadId,
config: QueryConfig(
cacheDuration: const Duration(days: 30),
refetchDuration: const Duration(minutes: 1),
refetchDuration: const Duration(minutes: 10),
storageDuration: const Duration(days: 30),
),
queryFn: () {

View file

@ -75,7 +75,7 @@ class TraktService {
'Authorization': 'Bearer $_token',
};
Future<List<LibraryItem>> getUpNextSeries({bool noUpNext = false}) async {
Future<List<LibraryItem>> getUpNextSeries() async {
await initStremioService();
if (!isEnabled()) {
@ -98,17 +98,6 @@ class TraktService {
final showId = show['show']['ids']['trakt'];
final imdb = show['show']['ids']['imdb'];
if (noUpNext == true) {
final meta = await stremioService!.getItemById(
Meta(
type: "series",
id: imdb,
),
);
return (meta as Meta).copyWith();
}
try {
final progressResponse = await http.get(
Uri.parse('$_baseUrl/shows/$showId/progress/watched'),
@ -123,11 +112,13 @@ class TraktService {
final nextEpisode = progress['next_episode'];
print(nextEpisode);
if (nextEpisode != null && imdb != null) {
final item = await stremioService!.getItemById(
Meta(type: "series", id: imdb),
Meta(
type: "series",
id: imdb,
externalIds: show['show']['ids'],
),
);
item as Meta;
@ -203,7 +194,7 @@ class TraktService {
}).toList(),
);
return result.map((res) {
return result.sublist(0, 20).map((res) {
Meta returnValue = res as Meta;
if (progress.containsKey(res.id)) {
@ -325,15 +316,23 @@ class TraktService {
final recommendedShows =
json.decode(recommendationsResponse.body) as List;
final result = await stremioService!.getBulkItem(
recommendedShows.map((show) {
final imdb = show['ids']['imdb'];
return Meta(
type: "series",
id: imdb,
);
}).toList(),
);
final result = (await stremioService!.getBulkItem(
recommendedShows
.map((show) {
final imdb = show['ids']?['imdb'];
if (imdb == null) {
return null;
}
return Meta(
type: "series",
id: imdb,
);
})
.whereType<Meta>()
.toList(),
));
return result;
} catch (e, stack) {

View file

@ -88,6 +88,11 @@ class MoreContainer extends StatelessWidget {
},
hideTrailing: true,
),
Text(
"Version ${const String.fromEnvironment('BUILD_ID', defaultValue: "Dev")}",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),

View file

@ -34,6 +34,10 @@ final Map<String, List<ExternalMediaPlayer>> externalPlayers = {
id: "com.brouken.player",
name: "JustPlayer",
),
ExternalMediaPlayer(
id: "xyz.skybox.player",
name: "Skybox",
),
],
"ios": [
ExternalMediaPlayer(

View file

@ -92,8 +92,8 @@ BEGIN
VALUE "CompanyName", "media.madari" "\0"
VALUE "FileDescription", "madari_client" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "madari_client" "\0"
VALUE "LegalCopyright", "Copyright (C) 2024 media.madari. All rights reserved." "\0"
VALUE "InternalName", "Madari" "\0"
VALUE "LegalCopyright", "Copyright (C) 2025 media.madari. All rights reserved." "\0"
VALUE "OriginalFilename", "madari_client.exe" "\0"
VALUE "ProductName", "madari_client" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"

View file

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.Create(L"madari_client", origin, size)) {
if (!window.Create(L"Madari", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);