Skip to content

Commit

Permalink
Merge pull request #47 from resucutie/dev
Browse files Browse the repository at this point in the history
Merge 1.6.0 with master
  • Loading branch information
resucutie authored Dec 30, 2024
2 parents b0caa50 + 5defa07 commit c14bbb5
Show file tree
Hide file tree
Showing 110 changed files with 5,721 additions and 2,570 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/flutter-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.22.0'
flutter-version: '3.24.5'
- name: Install compiler dependencies
run: sudo apt-get update && sudo apt-get install -y clang ninja-build libgtk-3-dev libmpv-dev mpv
- name: Install project dependencies
Expand Down Expand Up @@ -44,7 +44,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.22.0'
flutter-version: '3.24.5'
- name: Use pub version of media_kit_video
uses: fjogeleit/yaml-update-action@main
with:
Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.22.0'
flutter-version: '3.24.5'
- name: Install project dependencies
run: flutter pub get
- name: Enable macOS building
Expand All @@ -111,7 +111,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.22.0'
flutter-version: '3.24.5'
- name: Install project dependencies
run: flutter pub get
- name: Build artifacts
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
android:label="LocalBooru"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
android:requestLegacyExternalStorage="true"
android:enableOnBackInvokedCallback="false"> <!-- disabled due to go router having issues with predictive back gestures https://github.com/flutter/flutter/issues/138614#issuecomment-1909155402 -->
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
17 changes: 16 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '2.0.21'
repositories {
google()
mavenCentral()
Expand All @@ -21,6 +21,21 @@ rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
afterEvaluate { project ->
if (project.plugins.hasPlugin("com.android.application") ||
project.plugins.hasPlugin("com.android.library")) {
project.android {
compileSdkVersion 34
buildToolsVersion "34.0.0"
if (namespace == null) {
namespace project.group
}

}
}
}
}
subprojects {
project.evaluationDependsOn(':app')
}
Expand Down
4 changes: 3 additions & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
2 changes: 1 addition & 1 deletion android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "com.android.application" version "8.3.2" apply false
}

include ":app"
Binary file added assets/counter/signs/0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/counter/signs/og/9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/liberapay.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions dart_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
tags:
import:
single:
multi:
accurate:
identify:
download:
websites:

danbooru1:
moebooru:
danbooru2:
e621:
gelbooru025:
gelbooru020:
gelbooru01:
twitter:
furaffinity:
instagram:
2 changes: 2 additions & 0 deletions lib/api/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ library localbooru_api;

import 'dart:io';
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:localbooru/api/preset/index.dart';
import 'package:localbooru/utils/constants.dart';
import 'package:localbooru/utils/listeners.dart';
import 'package:localbooru/utils/misc.dart';
Expand Down
50 changes: 50 additions & 0 deletions lib/api/preset/autodownload/multi/image_boards.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
part of preset;

// danbooru2 and e621/e926 share the same api endpoints when it comes to pools
// danbooru 2: if you add .json at the end of the post url, it'll return the JSON of that post
Future<VirtualPresetCollection> _danbooru2LikeAPIs(Uri uri, Function(Uri uri, {HandleChunk handleChunk}) importer) async {
final res = await lbHttp.get(Uri.parse("${[uri.origin, uri.path].join("/")}.json"));
final json = jsonDecode(res.body);

final presets = await multiImageDownloader(
postsToIterate: List<int>.from(json["post_ids"]),
getter: (post, handler) {
return importer(Uri.parse([uri.origin, "posts", post].join("/")), handleChunk: handler,);
},
);

return VirtualPresetCollection(
name: (json["name"] as String).replaceAll("_", " "),
pages: addSourceToAllPresets(presets, [uri.origin, uri.path].join("/"))
);
}
Future<VirtualPresetCollection> danbooru2ToCollectionPreset(Uri uri) => _danbooru2LikeAPIs(uri, danbooru2ToPresetImage);
Future<VirtualPresetCollection> e621ToCollectionPreset(Uri uri) => _danbooru2LikeAPIs(uri, e621ToPresetImage);

// danbooru 1: /pool/show.xml?id=(id) returns all of the posts already parsed
Future<VirtualPresetCollection> danbooru1ToCollectionPreset(Uri uri) async {
final id = uri.pathSegments.last;
final res = await lbHttp.get(Uri.parse("${uri.origin}/pool/show.json?id=$id"));
final json = jsonDecode(res.body);

final presets = await multiImageDownloader(
postsToIterate: List<Map<String, dynamic>>.from(json["posts"]),
getter: (post, handler) {
return anyURLToPresetImage(post["file_url"], handleChunk: handler);
},
);

return VirtualPresetCollection(
name: (json["name"] as String).replaceAll("_", " "),
pages: addSourceToAllPresets(presets, [uri.origin, uri.path].join("/"))
);
}


List<PresetImage> addSourceToAllPresets(List<PresetImage> presets, String source) {
return presets.map((preset) {
if(preset.sources == null) preset.sources = [source];
else preset.sources!.add(source);
return preset;
},).toList();
}
57 changes: 57 additions & 0 deletions lib/api/preset/autodownload/multi/index.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:localbooru/api/preset/index.dart';
import 'package:localbooru/utils/download_image.dart';
import 'package:localbooru/utils/listeners.dart';

Future<List<PresetImage>> multiImageDownloader<T>({
required List<T> postsToIterate,
required Future<PresetImage> Function(T post, HandleChunk handler) getter,
Duration? debounce
}) async {
final completionist = MultiCompletionist(postsToIterate.length);

List<PresetImage> presets = [];

for(final (index, post) in postsToIterate.indexed) {
debugPrint("Adding post $post (${index + 1} of ${postsToIterate.length})");
presets.add(await getter(post, completionist.chunkHandler(index)));
if(debounce != null) await Future.delayed(debounce);
}

return presets;
}

class MultiCompletionist {
MultiCompletionist(this.amount);
final int amount;
final Map<dynamic, _Operation> _operations = {};

HandleChunk chunkHandler(dynamic id) {
return (chunk, res) {
if(res.contentLength == null) return;
if(!_operations.containsKey(id)) {
_operations[id] = _Operation(
downloaded: 0,
sizeOfContent: res.contentLength!
);
}
_operations[id]!.downloaded += chunk.length;
_updateProgress();
};
}

_updateProgress() {
final operations = _operations.values;
double finalVal = 0;
for(final operation in operations) {
finalVal += operation.downloaded / operation.sizeOfContent;
}
importListener.updateImportStatus(progress: finalVal / amount);
}
}

class _Operation {
_Operation({required this.downloaded, required this.sizeOfContent});
int downloaded;
int sizeOfContent;
}
41 changes: 41 additions & 0 deletions lib/api/preset/autodownload/single/art_directed.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
part of preset;

// furaffinity: it doesn't offer an api, but fxraffinity exists, and it bypasses the nsfw sign up wall, so we can extract its embed to
// obtain its image. the url nor fxraffinity's embed gives any clue about the poster, but furryaffinity's website title, as well as its
// embed title gives, so we just fetch those (and also bypasses the nsfw sign up wall)
Future<PresetImage> furaffinityToPresetImage(Uri uri) async {
final fxReq = Request("Get", Uri.parse(["https://fxraffinity.net", uri.path, "?full"].join()))..followRedirects = false;
final res = await Response.fromStream(await lbHttp.send(fxReq));
final websiteRes = await lbHttp.get(Uri.parse(["https://furaffinity.net", uri.path].join()));

final fileUrl = getMetaProperty(parse(res.body), property: "og:image");
if(fileUrl == null) throw "Could not grab image";

final title = getMetaProperty(parse(websiteRes.body), property: "og:title");

final downloadedFileInfo = await downloadFile(Uri.parse(fileUrl));

return PresetImage(
image: downloadedFileInfo,
sources: [["https://furaffinity.net", uri.path].join()],
tags: {
"artist": title != null ? [title.split(" ").last.toLowerCase()] : [],
}
);
}

// devianart: use their oEmbed API
Future<PresetImage> deviantartToPresetImage(String url) async {
final res = await lbHttp.get(Uri.parse(["https://backend.deviantart.com/oembed?url=", url].join()));
final json = jsonDecode(res.body);

final downloadedFileInfo = await downloadFile(Uri.parse(json["url"]));

return PresetImage(
image: downloadedFileInfo,
sources: [url],
tags: {
"artist": [json["author_name"].toLowerCase()],
}
);
}
37 changes: 37 additions & 0 deletions lib/api/preset/autodownload/single/generic.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
part of preset;

// twitter: fxtwitter offers a url to give only the image. getting the artist is as easy as reading the first path segment
Future<PresetImage> twitterToPresetImage(Uri uri) async {
// final res = await http.get(Uri.parse(["https://d.fxtwitter.com", uri.path].join()));

final downloadedFileInfo = await downloadFile(Uri.parse(["https://d.fxtwitter.com", uri.path].join()));

return PresetImage(
image: downloadedFileInfo,
sources: [["https://x.com", uri.path].join("")],
tags: {
"artist": List<String>.from([uri.pathSegments[0].toLowerCase()]),
}
);
}

// twitter: instafix offers a url to give only the image. getting the artist is as easy as reading the first path segment
Future<PresetImage> instagramToPresetImage(Uri uri) async {
final fxReq = Request("Get", Uri.parse(["https://ddinstagram.com", uri.path].join()))..followRedirects = false;
final response = await Response.fromStream(await lbHttp.send(fxReq));
final title = getMetaProperty(parse(response.body), property: "twitter:title");

debugPrint(response.body);

final downloadedFileInfo = await downloadFile(Uri.parse(["https://d.ddinstagram.com", uri.path].join()));

debugPrint(downloadedFileInfo.path);

return PresetImage(
image: downloadedFileInfo,
sources: [["https://instagram.com", uri.path].join("")],
tags: {
"artist": title != null ? [title.substring(1)] : [],
}
);
}
Loading

0 comments on commit c14bbb5

Please sign in to comment.