mirror of
https://gitlab.com/moepoi/neonime-app.git
synced 2024-11-15 03:26:24 +01:00
Add batch feature
This commit is contained in:
parent
ebb675e591
commit
371291616c
5 changed files with 384 additions and 7 deletions
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:neonime_app/pages/about.dart';
|
import 'package:neonime_app/pages/about.dart';
|
||||||
|
import 'package:neonime_app/pages/batch.dart';
|
||||||
|
|
||||||
class MainDrawer extends StatelessWidget {
|
class MainDrawer extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -27,7 +28,11 @@ class MainDrawer extends StatelessWidget {
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.library_books),
|
leading: Icon(Icons.library_books),
|
||||||
title: Text('Batch'),
|
title: Text('Batch'),
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => Batch(),
|
||||||
|
));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.schedule),
|
leading: Icon(Icons.schedule),
|
||||||
|
@ -38,11 +43,9 @@ class MainDrawer extends StatelessWidget {
|
||||||
leading: Icon(Icons.info),
|
leading: Icon(Icons.info),
|
||||||
title: Text('About'),
|
title: Text('About'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
context,
|
builder: (context) => About(),
|
||||||
MaterialPageRoute(
|
));
|
||||||
builder: (context) => About(),
|
|
||||||
));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:neonime_app/pages/anime-detail.dart';
|
import 'package:neonime_app/pages/anime-detail.dart';
|
||||||
|
import 'package:neonime_app/pages/batch-detail.dart';
|
||||||
import 'package:neonime_app/pages/episode-detail.dart';
|
import 'package:neonime_app/pages/episode-detail.dart';
|
||||||
import 'package:neonime_app/pages/movie-detail.dart';
|
import 'package:neonime_app/pages/movie-detail.dart';
|
||||||
import 'package:neonime_app/scrapper/search.dart';
|
import 'package:neonime_app/scrapper/search.dart';
|
||||||
|
@ -57,7 +58,13 @@ class _SearchState extends State<Search> {
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
} else if (data[index]['link'].contains('/batch/')) {
|
} else if (data[index]['link'].contains('/batch/')) {
|
||||||
// OTW BIKIN
|
Navigator.push(context, MaterialPageRoute(
|
||||||
|
builder: (context) => BatchDetail(),
|
||||||
|
settings: RouteSettings(arguments: {
|
||||||
|
'title': data[index]['title'],
|
||||||
|
'url': data[index]['link']
|
||||||
|
})
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
builder: (context) => MovieDetail(),
|
builder: (context) => MovieDetail(),
|
||||||
|
|
178
lib/pages/batch-detail.dart
Normal file
178
lib/pages/batch-detail.dart
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:async_loader/async_loader.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:neonime_app/scrapper/batch.dart';
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class BatchDetail extends StatelessWidget {
|
||||||
|
final GlobalKey<AsyncLoaderState> asyncLoaderState =
|
||||||
|
new GlobalKey<AsyncLoaderState>();
|
||||||
|
InAppWebViewController webView;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final argsData = ModalRoute.of(context).settings.arguments as Map;
|
||||||
|
final title = argsData['title'];
|
||||||
|
final postUrl = argsData['url'];
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(title),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: AsyncLoader(
|
||||||
|
key: asyncLoaderState,
|
||||||
|
initState: () async => await getBatchDetail(postUrl),
|
||||||
|
renderLoad: () => new CircularProgressIndicator(),
|
||||||
|
renderError: ([error]) {
|
||||||
|
print(error);
|
||||||
|
return Text(error.toString());
|
||||||
|
},
|
||||||
|
renderSuccess: ({data}) {
|
||||||
|
return Center(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 200,
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: data['image'],
|
||||||
|
placeholder: (context, url) => CupertinoActivityIndicator(),
|
||||||
|
errorWidget: (context, url, error) => Image.asset('lib/assets/image-error.jpg'),
|
||||||
|
fadeOutDuration: Duration(milliseconds: 5),
|
||||||
|
imageBuilder: (context, imageProvider) => Container(
|
||||||
|
width: 200,
|
||||||
|
height: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: imageProvider,
|
||||||
|
fit: BoxFit.cover
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
data['description'],
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Card(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
data['info'],
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Card(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
flex: 2,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(8),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: data['image'],
|
||||||
|
placeholder: (context, url) => CupertinoActivityIndicator(),
|
||||||
|
errorWidget: (context, url, error) => Image.asset('lib/assets/image-error.jpg'),
|
||||||
|
fadeOutDuration: Duration(milliseconds: 5),
|
||||||
|
imageBuilder: (context, imageProvider) => Container(
|
||||||
|
width: 120,
|
||||||
|
height: 130,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: imageProvider,
|
||||||
|
fit: BoxFit.cover
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
flex: 5,
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(5),
|
||||||
|
child: Text(
|
||||||
|
data['title'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18, fontWeight: FontWeight.w400),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: CupertinoButton.filled(
|
||||||
|
child: Text('Click to Download'),
|
||||||
|
onPressed: () {
|
||||||
|
List<CupertinoActionSheetAction> listButton =
|
||||||
|
List<CupertinoActionSheetAction>();
|
||||||
|
var counter = 0;
|
||||||
|
data['download_url'].forEach((link) {
|
||||||
|
counter++;
|
||||||
|
listButton.add(CupertinoActionSheetAction(
|
||||||
|
child: Text(
|
||||||
|
'Download #${counter.toString()}',
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showCupertinoDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return CupertinoAlertDialog(
|
||||||
|
title: Text('Your Download Link'),
|
||||||
|
content: Text(link),
|
||||||
|
actions: [
|
||||||
|
CupertinoDialogAction(
|
||||||
|
child: Text('Go'),
|
||||||
|
onPressed: () async {
|
||||||
|
await InAppBrowser
|
||||||
|
.openWithSystemBrowser(
|
||||||
|
url: link);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
barrierDismissible: true);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
});
|
||||||
|
if (listButton.length > 5) {
|
||||||
|
listButton.removeRange(5, listButton.length);
|
||||||
|
}
|
||||||
|
showCupertinoModalPopup(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return CupertinoActionSheet(
|
||||||
|
title: Text('Download Link'),
|
||||||
|
message: Text('Click the button to download'),
|
||||||
|
actions: listButton,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
129
lib/pages/batch.dart
Normal file
129
lib/pages/batch.dart
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:async_loader/async_loader.dart';
|
||||||
|
import 'package:neonime_app/scrapper/batch.dart';
|
||||||
|
import 'batch-detail.dart';
|
||||||
|
|
||||||
|
class Batch extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_BatchState createState() => _BatchState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BatchState extends State<Batch>
|
||||||
|
with AutomaticKeepAliveClientMixin<Batch> {
|
||||||
|
final GlobalKey<AsyncLoaderState> asyncLoaderState =
|
||||||
|
new GlobalKey<AsyncLoaderState>();
|
||||||
|
ScrollController _scrollController = new ScrollController();
|
||||||
|
List listData;
|
||||||
|
|
||||||
|
Future<Null> _handleRefresh() async {
|
||||||
|
asyncLoaderState.currentState.reloadState();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scrollController.addListener(() {
|
||||||
|
if (_scrollController.position.pixels ==
|
||||||
|
_scrollController.position.maxScrollExtent) {
|
||||||
|
_getMoreData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getMoreData() async {
|
||||||
|
final nextPage = (listData.length / 15) + 1;
|
||||||
|
List newData = await getBatch(nextPage.round().toString()).then((x) {
|
||||||
|
return x;
|
||||||
|
});
|
||||||
|
setState(() {
|
||||||
|
listData.addAll(newData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: Text('Batch'),),
|
||||||
|
body: Center(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
onRefresh: () => _handleRefresh(),
|
||||||
|
child: AsyncLoader(
|
||||||
|
key: asyncLoaderState,
|
||||||
|
initState: () async => await getBatch('1'),
|
||||||
|
renderLoad: () => new CircularProgressIndicator(),
|
||||||
|
renderError: ([error]) {
|
||||||
|
print(error);
|
||||||
|
return Text(error.toString());
|
||||||
|
},
|
||||||
|
renderSuccess: ({data}) {
|
||||||
|
listData = data;
|
||||||
|
return ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
|
itemCount: data.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BatchDetail(),
|
||||||
|
settings: RouteSettings(arguments: {
|
||||||
|
'title': listData[index]['title'],
|
||||||
|
'url': listData[index]['link']
|
||||||
|
})));
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: listData[index]['image'],
|
||||||
|
placeholder: (context, url) => CupertinoActivityIndicator(),
|
||||||
|
errorWidget: (context, url, error) => Image.asset('lib/assets/image-error.jpg'),
|
||||||
|
fadeOutDuration: Duration(milliseconds: 5),
|
||||||
|
imageBuilder: (context, imageProvider) => Container(
|
||||||
|
width: 400,
|
||||||
|
height: 210,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: imageProvider,
|
||||||
|
fit: BoxFit.cover
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
listData[index]['title'],
|
||||||
|
style: TextStyle(fontSize: 20),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
}
|
60
lib/scrapper/batch.dart
Normal file
60
lib/scrapper/batch.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
|
||||||
|
Future<dynamic> getBatch(String page) async {
|
||||||
|
try {
|
||||||
|
List<String> images = <String>[];
|
||||||
|
List<String> links = <String>[];
|
||||||
|
List<String> titles = <String>[];
|
||||||
|
final response = await http.get('https://neonime.moe/batch/page/$page/');
|
||||||
|
var document = parse(response.body);
|
||||||
|
var items = document.getElementsByClassName('item');
|
||||||
|
items.forEach((item) {
|
||||||
|
images.add(item.getElementsByTagName('img')[0].attributes['data-wpfc-original-src']);
|
||||||
|
links.add(item.getElementsByTagName('a')[0].attributes['href']);
|
||||||
|
titles.add(item.getElementsByClassName('title')[0].text);
|
||||||
|
});
|
||||||
|
var data = <dynamic>[];
|
||||||
|
for (var i = 0; i < links.length; i++) {
|
||||||
|
data.add({
|
||||||
|
'image': images[i].replaceAll(new RegExp(r'\/w(\d\d\d)\/'), '/original/'),
|
||||||
|
'link': links[i],
|
||||||
|
'title': titles[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
throw FormatException('ntah lah');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> getBatchDetail(String url) async {
|
||||||
|
try {
|
||||||
|
List<String> info = <String>[];
|
||||||
|
List<String> downloadUrls = <String>[];
|
||||||
|
final response = await http.get(url);
|
||||||
|
var document = parse(response.body);
|
||||||
|
var contentBox = document.getElementsByClassName('entry-content')[0];
|
||||||
|
var title = contentBox.getElementsByTagName('h1')[0].text;
|
||||||
|
var image = contentBox.getElementsByTagName('img')[0].attributes['data-wpfc-original-src'];
|
||||||
|
var description = contentBox.getElementsByTagName('p');
|
||||||
|
contentBox.getElementsByClassName('spaceit').forEach((x) {
|
||||||
|
info.add(x.text);
|
||||||
|
});
|
||||||
|
contentBox.getElementsByClassName('smokeurl')[0].getElementsByTagName('a').forEach((x) {
|
||||||
|
downloadUrls.add(x.attributes['href']);
|
||||||
|
});
|
||||||
|
final data = {
|
||||||
|
'title': title,
|
||||||
|
'description': description[2].text.length > 100 ? description[2].text : description[3].text,
|
||||||
|
'info': info.join('\n\n'),
|
||||||
|
'image': image.replaceAll(new RegExp(r'\/w(\d\d\d)\/'), '/original/'),
|
||||||
|
'download_url': downloadUrls
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
throw FormatException('ntah lah');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue