Compare commits

..

6 Commits

Author SHA1 Message Date
dom
8d312d8cf1 build
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-04 11:38:13 +08:00
dom
6738142ac0 opt player listener
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-04 11:19:11 +08:00
dom
3d99e6c761 fetch dyn type onlyfansQaCard
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-04 11:19:11 +08:00
dom
f9f52e918a resize image placeholder
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-04 11:19:11 +08:00
dom
6108290b4b remove unused property
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-04 11:19:05 +08:00
dom
8bae275120 fix trackpad not respecting enableSlideVolumeBrightness property
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-03 18:38:18 +08:00
15 changed files with 110 additions and 73 deletions

View File

@@ -46,7 +46,7 @@ jobs:
ln -sf ./build/ios/iphoneos Payload
# make AltSign happy...
find Payload/Runner.app/Frameworks -type d -name "*.framework" -exec codesign --force --sign - --preserve-metadata=identifier,entitlements {} \;
zip -r9 PiliPlus_ios_${{env.version}}.ipa Payload/runner.app
zip -r9 PiliPlus_ios_${{env.version}}.ipa Payload/Runner.app
- name: Release
if: ${{ github.event.inputs.tag != '' }}

View File

@@ -51,6 +51,17 @@ jobs:
shell: pwsh
run: lib/scripts/build.ps1
- name: pick tooltip fix
working-directory: ${{ env.FLUTTER_ROOT }}
run: |
git config --global user.name "ci"
git config --global user.email "example@example.com"
git stash || true
git cherry-pick 56956c33ef102ac0b5fc46b62bd2dd9f50a86616 --no-edit
git reset --soft HEAD~1
git stash pop || true
continue-on-error: true
- name: apply modal barrier patch
working-directory: ${{ env.FLUTTER_ROOT }}
run: git apply $GITHUB_WORKSPACE/lib/scripts/modal_barrier_patch.diff || true

View File

@@ -30,6 +30,17 @@ jobs:
shell: pwsh
run: lib/scripts/build.ps1
- name: pick tooltip fix
working-directory: ${{ env.FLUTTER_ROOT }}
run: |
git config --global user.name "ci"
git config --global user.email "example@example.com"
git stash || true
git cherry-pick 56956c33ef102ac0b5fc46b62bd2dd9f50a86616 --no-edit
git reset --soft HEAD~1
git stash pop || true
continue-on-error: true
- name: apply modal barrier patch
working-directory: ${{ env.FLUTTER_ROOT }}
run: git apply $GITHUB_WORKSPACE/lib/scripts/modal_barrier_patch.diff || true

View File

@@ -26,6 +26,17 @@ jobs:
channel: stable
flutter-version-file: pubspec.yaml
- name: pick tooltip fix
working-directory: ${{ env.FLUTTER_ROOT }}
run: |
git config --global user.name "ci"
git config --global user.email "example@example.com"
git stash || true
git cherry-pick 56956c33ef102ac0b5fc46b62bd2dd9f50a86616 --no-edit
git reset --soft HEAD~1
git stash pop || true
continue-on-error: true
- name: apply modal barrier patch
working-directory: ${{ env.FLUTTER_ROOT }}
run: git apply $env:GITHUB_WORKSPACE\lib\scripts\modal_barrier_patch.diff || true

View File

@@ -66,6 +66,9 @@ abstract final class Constants {
static const goodsUrlPrefix = "https://gaoneng.bilibili.com/tetris";
// 'itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme,sunflowerStyle,cardsEnhance,eva3CardOpus,eva3CardVideo,eva3CardComment,eva3CardVote,eva3CardUser'
static const dynFeatures = 'itemOpusStyle,listOnlyfans,onlyfansQaCard';
// 超分辨率滤镜
static const List<String> mpvAnime4KShaders = [
'Anime4K_Clamp_Highlights.glsl',

View File

@@ -60,7 +60,6 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
required this.forceMaterialTransparency,
required this.useDefaultSemanticsOrder,
required this.clipBehavior,
required this.accessibleNavigation,
required this.actionsPadding,
}) : assert(primary || topPadding == 0.0),
_bottomHeight = bottom?.preferredSize.height ?? 0.0;
@@ -97,7 +96,6 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final bool forceMaterialTransparency;
final bool useDefaultSemanticsOrder;
final Clip? clipBehavior;
final bool accessibleNavigation;
final EdgeInsetsGeometry? actionsPadding;
@override
@@ -192,7 +190,6 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
systemOverlayStyle != oldDelegate.systemOverlayStyle ||
forceMaterialTransparency != oldDelegate.forceMaterialTransparency ||
useDefaultSemanticsOrder != oldDelegate.useDefaultSemanticsOrder ||
accessibleNavigation != oldDelegate.accessibleNavigation ||
actionsPadding != oldDelegate.actionsPadding;
}
@@ -348,7 +345,6 @@ class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
forceMaterialTransparency: widget.forceMaterialTransparency,
useDefaultSemanticsOrder: widget.useDefaultSemanticsOrder,
clipBehavior: widget.clipBehavior,
accessibleNavigation: MediaQuery.of(context).accessibleNavigation,
actionsPadding: widget.actionsPadding,
),
),

View File

@@ -403,8 +403,12 @@ class _GalleryViewerState extends State<GalleryViewer>
return child;
} else {
return Image(
image: CachedNetworkImageProvider(
ImageUtils.thumbnailUrl(item.url, widget.quality),
image: ResizeImage.resizeIfNeeded(
_containerSize.width.cacheSize(context),
null,
CachedNetworkImageProvider(
ImageUtils.thumbnailUrl(item.url, widget.quality),
),
),
minScale: widget.minScale,
maxScale: widget.maxScale,

View File

@@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/pair.dart';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/constants.dart';
@@ -43,7 +44,7 @@ abstract final class DynamicsHttp {
'timezone_offset': '-480',
},
'offset': offset,
'features': 'itemOpusStyle,listOnlyfans',
'features': Constants.dynFeatures,
};
final res = await Request().get(Api.followDynamic, queryParameters: data);
final code = res.data['code'];
@@ -254,7 +255,7 @@ abstract final class DynamicsHttp {
'id': ?id,
'rid': ?rid,
'type': ?type,
'features': 'itemOpusStyle',
'features': Constants.dynFeatures,
'gaia_source': 'Athena',
'web_location': '333.1330',
'x-bili-device-req-json':
@@ -442,8 +443,7 @@ abstract final class DynamicsHttp {
'offset': offset,
'page_size': 20,
'source': 'Web',
// itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,decorationCard
'features': 'itemOpusStyle,listOnlyfans',
'features': Constants.dynFeatures,
},
);
if (res.data['code'] == 0) {

View File

@@ -405,7 +405,7 @@ abstract final class MemberHttp {
'offset': offset ?? '',
'host_mid': mid,
'timezone_offset': '-480',
'features': 'itemOpusStyle,listOnlyfans',
'features': Constants.dynFeatures,
'platform': 'web',
'web_location': '333.1387',
'dm_img_list': '[]',
@@ -457,7 +457,7 @@ abstract final class MemberHttp {
'page': pn,
'offset': offset,
'keyword': keyword,
'features': 'itemOpusStyle,listOnlyfans',
'features': Constants.dynFeatures,
'web_location': 333.1387,
},
);

View File

@@ -867,6 +867,7 @@ class DynamicMajorModel {
String? type;
DynamicArchiveModel? courses;
Common? common;
Common? upowerCommon;
Music? music;
ModuleBlocked? blocked;
Medialist? medialist;
@@ -900,6 +901,9 @@ class DynamicMajorModel {
? null
: DynamicArchiveModel.fromJson(json['courses']);
common = json['common'] == null ? null : Common.fromJson(json['common']);
upowerCommon = json['upower_common'] == null
? null
: Common.fromJson(json['upower_common']);
music = json['music'] == null ? null : Music.fromJson(json['music']);
blocked = json['blocked'] == null
? null

View File

@@ -300,8 +300,9 @@ class AudioController extends GetxController
player = null;
return;
}
final stream = player!.stream;
_subscriptions = [
player!.stream.position.listen((position) {
stream.position.listen((position) {
if (isDragging) return;
if (position.inSeconds != this.position.value.inSeconds) {
this.position.value = position;
@@ -309,8 +310,8 @@ class AudioController extends GetxController
videoPlayerServiceHandler?.onPositionChange(position);
}
}),
player!.stream.duration.listen(duration.call),
player!.stream.playing.listen((playing) {
stream.duration.listen(duration.call),
stream.playing.listen((playing) {
final PlayerStatus playerStatus;
if (playing) {
animController.forward();
@@ -321,7 +322,7 @@ class AudioController extends GetxController
}
videoPlayerServiceHandler?.onStatusChange(playerStatus, false, false);
}),
player!.stream.completed.listen((completed) {
stream.completed.listen((completed) {
_videoDetailController?.playedTime = duration.value;
videoPlayerServiceHandler?.onStatusChange(
PlayerStatus.completed,
@@ -774,6 +775,7 @@ class AudioController extends GetxController
..onSeek = null
..onVideoDetailDispose(hashCode.toString());
_subscriptions?.forEach((e) => e.cancel());
_subscriptions?.clear();
_subscriptions = null;
player?.dispose();
player = null;

View File

@@ -112,6 +112,8 @@ Widget module(
);
// 活动
case 'DYNAMIC_TYPE_COMMON_SQUARE':
final common = major?.common ?? major?.upowerCommon;
if (common == null) return const SizedBox.shrink();
return Material(
color: floor == 1
? theme.dividerColor.withValues(alpha: 0.08)
@@ -123,7 +125,7 @@ Widget module(
borderRadius: floor == 1 ? null : StyleString.mdRadius,
onTap: () {
try {
String url = major.common!.jumpUrl!;
String url = common.jumpUrl!;
if (url.contains('bangumi/play') &&
PageUtils.viewPgcFromUri(url)) {
return;
@@ -141,14 +143,7 @@ Widget module(
child: Row(
spacing: 10,
children: [
if (item
.modules
.moduleDynamic!
.major!
.common!
.cover
?.isNotEmpty ==
true)
if (common.cover?.isNotEmpty ?? false)
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(6)),
child: CachedNetworkImage(
@@ -156,9 +151,7 @@ Widget module(
height: 45,
fit: BoxFit.cover,
memCacheWidth: 45.cacheSize(context),
imageUrl: ImageUtils.safeThumbnailUrl(
item.modules.moduleDynamic!.major!.common!.cover,
),
imageUrl: ImageUtils.safeThumbnailUrl(common.cover),
),
),
Expanded(
@@ -166,22 +159,16 @@ Widget module(
spacing: 2,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
major!.common!.title!,
style: TextStyle(color: theme.colorScheme.primary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (item
.modules
.moduleDynamic!
.major!
.common!
.desc
?.isNotEmpty ==
true)
if (common.title?.isNotEmpty ?? false)
Text(
major.common!.desc!,
common.title!,
style: TextStyle(color: theme.colorScheme.primary),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (common.desc?.isNotEmpty ?? false)
Text(
common.desc!,
style: TextStyle(
color: theme.colorScheme.outline,
fontSize: theme.textTheme.labelMedium!.fontSize,

View File

@@ -635,7 +635,6 @@ class VideoDetailController extends GetxController
_autoPlay.value = true;
playedTime = plPlayerController.position;
plPlayerController
..removeListeners()
..isBuffering.value = false
..buffered.value = Duration.zero;

View File

@@ -63,6 +63,8 @@ import 'package:path/path.dart' as path;
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:window_manager/window_manager.dart';
typedef PlayCallback = Future<void>? Function();
class PlPlayerController with BlockConfigMixin {
Player? _videoPlayerController;
VideoController? _videoController;
@@ -484,11 +486,11 @@ class PlPlayerController with BlockConfigMixin {
return _instance != null;
}
static void setPlayCallBack(Future<void>? Function()? playCallBack) {
static void setPlayCallBack(PlayCallback? playCallBack) {
_playCallBack = playCallBack;
}
static Future<void>? Function()? _playCallBack;
static PlayCallback? _playCallBack;
static Future<void>? playIfExists() {
// await _instance?.play(repeat: repeat, hideControls: hideControls);
@@ -625,6 +627,7 @@ class PlPlayerController with BlockConfigMixin {
await _createVideoController(dataSource, seekTo, volume);
if (_playerCount == 0) {
_removeListeners();
_videoPlayerController?.dispose();
_videoPlayerController = null;
_videoController = null;
@@ -640,8 +643,6 @@ class PlPlayerController with BlockConfigMixin {
// 数据加载完成
dataStatus.value = DataStatus.loaded;
// listen the video player events
startListeners();
await _initializePlayer();
onInit?.call();
} catch (err, stackTrace) {
@@ -751,6 +752,9 @@ class PlPlayerController with BlockConfigMixin {
referer: HttpString.baseUrl,
);
// await player.setAudioTrack(.auto());
_startListeners(player);
return player;
}
@@ -760,8 +764,6 @@ class PlPlayerController with BlockConfigMixin {
Duration? seekTo,
Volume? volume,
) async {
// 每次配置时先移除监听
removeListeners();
isBuffering.value = false;
buffered.value = Duration.zero;
_heartDuration = 0;
@@ -774,6 +776,7 @@ class PlPlayerController with BlockConfigMixin {
if (player == null) {
player = await _initPlayer();
if (_playerCount == 0) {
_removeListeners();
player.dispose();
player = null;
_videoController = null;
@@ -916,15 +919,16 @@ class PlPlayerController with BlockConfigMixin {
return null;
}
List<StreamSubscription> subscriptions = [];
final Set<Function(Duration position)> _positionListeners = {};
final Set<Function(PlayerStatus status)> _statusListeners = {};
List<StreamSubscription>? _subscriptions;
final Set<ValueChanged<Duration>> _positionListeners = {};
final Set<ValueChanged<PlayerStatus>> _statusListeners = {};
/// 播放事件监听
void startListeners() {
final controllerStream = videoPlayerController!.stream;
subscriptions = [
controllerStream.playing.listen((event) {
void _startListeners(NativePlayer player) {
assert(_subscriptions == null);
final stream = player.stream;
_subscriptions = [
stream.playing.listen((event) {
WakelockPlus.toggle(enable: event);
if (event) {
if (_shouldSetPip) {
@@ -953,7 +957,7 @@ class PlPlayerController with BlockConfigMixin {
makeHeartBeat(positionSeconds.value, type: HeartBeatType.status);
}
}),
controllerStream.completed.listen((event) {
stream.completed.listen((event) {
if (event) {
playerStatus.value = PlayerStatus.completed;
@@ -966,7 +970,7 @@ class PlPlayerController with BlockConfigMixin {
}
makeHeartBeat(positionSeconds.value, type: HeartBeatType.completed);
}),
controllerStream.position.listen((event) {
stream.position.listen((event) {
position = event;
updatePositionSecond();
if (!isSliderMoving.value) {
@@ -980,14 +984,14 @@ class PlPlayerController with BlockConfigMixin {
}
makeHeartBeat(event.inSeconds);
}),
controllerStream.duration.listen((Duration event) {
stream.duration.listen((Duration event) {
duration.value = event;
}),
controllerStream.buffer.listen((Duration event) {
stream.buffer.listen((Duration event) {
buffered.value = event;
updateBufferedSecond();
}),
controllerStream.buffering.listen((bool event) {
stream.buffering.listen((bool event) {
isBuffering.value = event;
videoPlayerServiceHandler?.onStatusChange(
playerStatus.value,
@@ -996,14 +1000,14 @@ class PlPlayerController with BlockConfigMixin {
);
}),
if (kDebugMode)
controllerStream.log.listen(((PlayerLog log) {
stream.log.listen(((PlayerLog log) {
if (log.level == 'error' || log.level == 'fatal') {
Utils.reportError('${log.level}: ${log.prefix}: ${log.text}', null);
} else {
debugPrint(log.toString());
}
})),
controllerStream.error.listen((String event) {
stream.error.listen((String event) {
if (dataSource is FileSource &&
event.startsWith("Failed to open file")) {
return;
@@ -1078,8 +1082,10 @@ class PlPlayerController with BlockConfigMixin {
}
/// 移除事件监听
Future<void> removeListeners() {
return Future.wait(subscriptions.map((e) => e.cancel()));
void _removeListeners() {
_subscriptions?.forEach((e) => e.cancel());
_subscriptions?.clear();
_subscriptions = null;
}
void _cancelSubForSeek() {
@@ -1488,20 +1494,20 @@ class PlPlayerController with BlockConfigMixin {
}
}
void addPositionListener(Function(Duration position) listener) {
void addPositionListener(ValueChanged<Duration> listener) {
if (_playerCount == 0) return;
_positionListeners.add(listener);
}
void removePositionListener(Function(Duration position) listener) =>
void removePositionListener(ValueChanged<Duration> listener) =>
_positionListeners.remove(listener);
void addStatusLister(Function(PlayerStatus status) listener) {
void addStatusLister(ValueChanged<PlayerStatus> listener) {
if (_playerCount == 0) return;
_statusListeners.add(listener);
}
void removeStatusLister(Function(PlayerStatus status) listener) =>
void removeStatusLister(ValueChanged<PlayerStatus> listener) =>
_statusListeners.remove(listener);
// 记录播放记录
@@ -1625,8 +1631,7 @@ class PlPlayerController with BlockConfigMixin {
windowManager.setAlwaysOnTop(false);
}
removeListeners();
subscriptions.clear();
_removeListeners();
_positionListeners.clear();
_statusListeners.clear();
if (playerStatus.isPlaying) {

View File

@@ -1327,6 +1327,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
plPlayerController.updatePreviewIndex(newPos ~/ 1000);
}
} else if (_gestureType == GestureType.right) {
if (!plPlayerController.enableSlideVolumeBrightness) {
return;
}
final double level = maxHeight * 0.5;
EasyThrottle.throttle(
'setVolume',