added tmdb url handling

This commit is contained in:
Parsalp
2025-03-05 13:43:31 +03:30
parent 293cd6eee8
commit 19d3f8491f
9 changed files with 223 additions and 1 deletions

View File

@ -24,6 +24,40 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Deep linking for movies -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="www.themoviedb.org"
android:pathPrefix="/movie" />
</intent-filter>
<!-- Deep linking for TV shows -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="www.themoviedb.org"
android:pathPrefix="/tv" />
</intent-filter>
<!-- Custom URI scheme for movies -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="themoviedb"
android:host="movie" />
</intent-filter>
<!-- Custom URI scheme for TV shows -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="themoviedb"
android:host="tv" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

View File

@ -0,0 +1,110 @@
import 'dart:io';
import 'package:Mirarr/functions/fetchers/fetch_movie_details.dart';
import 'package:Mirarr/functions/fetchers/fetch_serie_details.dart';
import 'package:Mirarr/functions/regionprovider_class.dart';
import 'package:Mirarr/moviesPage/functions/on_tap_movie_desktop.dart';
import 'package:Mirarr/seriesPage/function/on_tap_serie_desktop.dart';
import 'package:flutter/material.dart';
import 'package:Mirarr/moviesPage/functions/on_tap_movie.dart';
import 'package:Mirarr/seriesPage/function/on_tap_serie.dart';
import 'package:provider/provider.dart';
class TMDBUrlParser {
static bool isTMDBMovieUrl(String url) {
return url.startsWith('https://www.themoviedb.org/movie/') ||
url.startsWith('themoviedb://movie/');
}
static bool isTMDBTVUrl(String url) {
return url.startsWith('https://www.themoviedb.org/tv/') ||
url.startsWith('themoviedb://tv/');
}
static Future<String> _getMovieTitle(
int movieId, BuildContext context) async {
final region =
Provider.of<RegionProvider>(context, listen: false).currentRegion;
final responseData = await fetchMovieDetails(movieId, region);
return responseData['title'];
}
static int? parseMovieId(String url) {
try {
// Remove the base URL part
String path = url.replaceAll('https://www.themoviedb.org/movie/', '');
path = path.replaceAll('themoviedb://movie/', '');
// Split by dash to separate ID and title
List<String> parts = path.split('-');
// First part is the ID
return int.parse(parts[0]);
} catch (e) {
debugPrint('Error parsing TMDB movie ID: $e');
return null;
}
}
static Future<String> _getSerieTitle(
int serieId, BuildContext context) async {
final region =
Provider.of<RegionProvider>(context, listen: false).currentRegion;
final responseData = await fetchSerieDetails(serieId, region);
return responseData['name'];
}
static int? parseSerieId(String url) {
try {
// Remove the base URL part
String path = url.replaceAll('https://www.themoviedb.org/tv/', '');
path = path.replaceAll('themoviedb://tv/', '');
// Split by dash to separate ID and title
List<String> parts = path.split('-');
// First part is the ID
int serieId = int.parse(parts[0]);
return serieId;
} catch (e) {
debugPrint('Error parsing TMDB TV URL: $e');
return null;
}
}
static Future<void> handleUrl(String url, BuildContext context) async {
// Ensure we're on the main thread and the context is valid
if (!context.mounted) return;
if (isTMDBMovieUrl(url)) {
final movieId = parseMovieId(url);
if (movieId != null) {
try {
final movieTitle = await _getMovieTitle(movieId, context);
if (context.mounted) {
if (Platform.isAndroid || Platform.isIOS) {
onTapMovie(movieTitle, movieId, context);
} else {
onTapMovieDesktop(movieTitle, movieId, context);
}
}
} catch (e) {
debugPrint('Error fetching movie title: $e');
}
}
} else if (isTMDBTVUrl(url)) {
final serieId = parseSerieId(url);
if (serieId != null) {
final serieTitle = await _getSerieTitle(serieId, context);
if (context.mounted) {
if (Platform.isAndroid || Platform.isIOS) {
onTapSerie(serieTitle, serieId, context);
} else {
onTapSerieDesktop(serieTitle, serieId, context);
}
}
}
}
}
}

View File

@ -1,5 +1,6 @@
import 'package:Mirarr/functions/themeprovider_class.dart';
import 'package:Mirarr/functions/regionprovider_class.dart';
import 'package:Mirarr/functions/url_parser.dart';
import 'package:Mirarr/widgets/check_updates.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@ -7,9 +8,12 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:Mirarr/moviesPage/mainPage.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:provider/provider.dart';
import 'package:app_links/app_links.dart';
import 'dart:io';
import 'package:window_manager/window_manager.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env");
@ -39,13 +43,61 @@ void main() async {
);
}
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late AppLinks _appLinks;
bool _isInitialized = false;
@override
void initState() {
super.initState();
_initAppLinks();
}
Future<void> _initAppLinks() async {
if (!_isInitialized && !Platform.isLinux) {
_appLinks = AppLinks();
// Handle initial URI if the app was launched from a link
try {
final uri = await _appLinks.getInitialAppLink();
if (uri != null) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (navigatorKey.currentContext != null) {
await TMDBUrlParser.handleUrl(
uri.toString(), navigatorKey.currentContext!);
}
});
}
} catch (e) {
debugPrint('Error handling initial app link: $e');
}
// Handle incoming links while the app is running
_appLinks.uriLinkStream.listen((uri) async {
if (uri != null && navigatorKey.currentContext != null) {
await TMDBUrlParser.handleUrl(
uri.toString(), navigatorKey.currentContext!);
}
}, onError: (err) {
debugPrint('Error handling app links: $err');
});
_isInitialized = true;
}
}
@override
Widget build(BuildContext context) {
return Consumer<ThemeProvider>(builder: (context, themeProvider, child) {
return MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Mirarr',
theme: themeProvider.currentTheme,

View File

@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
#include <gtk/gtk_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) gtk_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
gtk
screen_retriever
url_launcher_linux
window_manager

View File

@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
app_links:
dependency: "direct main"
description:
name: app_links
sha256: "3ced568a5d9e309e99af71285666f1f3117bddd0bd5b3317979dccc1a40cada4"
url: "https://pub.dev"
source: hosted
version: "3.5.1"
args:
dependency: transitive
description:
@ -216,6 +224,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
gtk:
dependency: transitive
description:
name: gtk
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
url: "https://pub.dev"
source: hosted
version: "2.1.0"
hive:
dependency: "direct main"
description:

View File

@ -27,6 +27,7 @@ dependencies:
cupertino_icons: ^1.0.2
provider: ^6.1.2
shared_preferences: ^2.0.15
app_links: ^3.5.0
dev_dependencies:
flutter_test:

View File

@ -6,12 +6,15 @@
#include "generated_plugin_registrant.h"
#include <app_links/app_links_plugin_c_api.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
ScreenRetrieverPluginRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
app_links
connectivity_plus
screen_retriever
url_launcher_windows