mirror of
https://gitlab.com/moepoi/neonime-app.git
synced 2024-11-23 23:38:29 +01:00
Merge branch '1-add-batch-feature' into 'master'
Resolve "Add batch feature" Closes #1 See merge request moepoi/neonime-app!1
This commit is contained in:
commit
b7d7f425fc
5 changed files with 384 additions and 7 deletions
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:neonime_app/pages/about.dart';
|
||||
import 'package:neonime_app/pages/batch.dart';
|
||||
|
||||
class MainDrawer extends StatelessWidget {
|
||||
@override
|
||||
|
@ -27,7 +28,11 @@ class MainDrawer extends StatelessWidget {
|
|||
ListTile(
|
||||
leading: Icon(Icons.library_books),
|
||||
title: Text('Batch'),
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => Batch(),
|
||||
));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.schedule),
|
||||
|
@ -38,11 +43,9 @@ class MainDrawer extends StatelessWidget {
|
|||
leading: Icon(Icons.info),
|
||||
title: Text('About'),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => About(),
|
||||
));
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => About(),
|
||||
));
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
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/movie-detail.dart';
|
||||
import 'package:neonime_app/scrapper/search.dart';
|
||||
|
@ -57,7 +58,13 @@ class _SearchState extends State<Search> {
|
|||
})
|
||||
));
|
||||
} 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 {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
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