26 changed files with 2375 additions and 52 deletions
@ -0,0 +1,379 @@ |
|||
Flutter crash report. |
|||
Please report a bug at https://github.com/flutter/flutter/issues. |
|||
|
|||
## command |
|||
|
|||
flutter build ios |
|||
|
|||
## exception |
|||
|
|||
ProcessException: ProcessException: Process exited abnormally with exit code -6: |
|||
Command line invocation: |
|||
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -list |
|||
|
|||
User defaults from command line: |
|||
IDEPackageSupportUseBuiltinSCM = YES |
|||
|
|||
Resolve Package Graph |
|||
|
|||
Fetching from https://github.com/apple/swift-log.git (cached) |
|||
|
|||
Fetching from https://github.com/apple/swift-protobuf.git (cached) |
|||
|
|||
Fetching from https://github.com/firebase/leveldb.git (cached) |
|||
|
|||
Fetching from https://github.com/google/abseil-cpp-binary.git (cached) |
|||
|
|||
Fetching from https://github.com/apple/swift-http-types (cached) |
|||
|
|||
Fetching from https://github.com/google/app-check.git (cached) |
|||
|
|||
Fetching from https://github.com/google/interop-ios-for-google-sdks.git (cached) |
|||
|
|||
Fetching from https://github.com/SDWebImage/SDWebImage (cached) |
|||
|
|||
Fetching from https://github.com/ybrid/opus-swift.git (cached) |
|||
|
|||
Fetching from https://github.com/google/GTMAppAuth.git (cached) |
|||
|
|||
Fetching from https://github.com/google/GoogleSignIn-iOS.git (cached) |
|||
|
|||
Fetching from https://github.com/TimOliver/TOCropViewController (cached) |
|||
|
|||
Fetching from https://github.com/zhangao0086/DKImagePickerController (cached) |
|||
|
|||
Fetching from https://github.com/zhangao0086/DKCamera (cached) |
|||
|
|||
Fetching from https://github.com/google/gtm-session-fetcher.git (cached) |
|||
|
|||
Fetching from https://github.com/google/GoogleDataTransport.git (cached) |
|||
|
|||
Fetching from https://github.com/firebase/flutterfire (cached) |
|||
|
|||
Fetching from https://github.com/SnapKit/SnapKit.git (cached) |
|||
|
|||
Fetching from https://github.com/apple/swift-openapi-runtime (cached) |
|||
|
|||
Fetching from https://github.com/Alamofire/Alamofire.git (cached) |
|||
|
|||
Fetching from https://github.com/apple/swift-system.git (cached) |
|||
|
|||
Fetching from https://github.com/firebase/nanopb.git (cached) |
|||
|
|||
Fetching from https://github.com/google/promises.git (cached) |
|||
|
|||
Fetching from https://github.com/google/GoogleAppMeasurement.git (cached) |
|||
|
|||
Fetching from https://github.com/kirualex/SwiftyGif.git (cached) |
|||
|
|||
Fetching from https://github.com/google/grpc-binary.git (cached) |
|||
|
|||
Fetching from https://github.com/google/GoogleUtilities.git (cached) |
|||
|
|||
Fetching from https://github.com/MacPaw/OpenAI.git (cached) |
|||
|
|||
Fetching from https://github.com/zhangao0086/DKPhotoGallery (cached) |
|||
|
|||
Fetching from https://github.com/loopwork-ai/eventsource.git (cached) |
|||
|
|||
Fetching from https://github.com/openid/AppAuth-iOS.git (cached) |
|||
|
|||
Fetching from https://github.com/modelcontextprotocol/swift-sdk.git (cached) |
|||
|
|||
Fetching from https://github.com/firebase/firebase-ios-sdk (cached) |
|||
|
|||
Creating working copy of package ‘abseil-cpp-binary’ |
|||
|
|||
Checking out 1.2024072200.0 of package ‘abseil-cpp-binary’ |
|||
|
|||
Creating working copy of package ‘app-check’ |
|||
|
|||
Checking out 11.2.0 of package ‘app-check’ |
|||
|
|||
Creating working copy of package ‘opus-swift’ |
|||
|
|||
Checking out 0.8.0 of package ‘opus-swift’ |
|||
|
|||
Creating working copy of package ‘SDWebImage’ |
|||
|
|||
Checking out 5.21.0 of package ‘SDWebImage’ |
|||
|
|||
Creating working copy of package ‘swift-http-types’ |
|||
|
|||
Checking out 1.4.0 of package ‘swift-http-types’ |
|||
|
|||
Creating working copy of package ‘interop-ios-for-google-sdks’ |
|||
|
|||
Checking out 101.0.0 of package ‘interop-ios-for-google-sdks’ |
|||
|
|||
Creating working copy of package ‘leveldb’ |
|||
|
|||
Checking out 1.22.5 of package ‘leveldb’ |
|||
|
|||
Creating working copy of package ‘GTMAppAuth’ |
|||
|
|||
Checking out 4.1.1 of package ‘GTMAppAuth’ |
|||
|
|||
Creating working copy of package ‘swift-log’ |
|||
|
|||
Checking out 1.6.3 of package ‘swift-log’ |
|||
|
|||
Creating working copy of package ‘swift-protobuf’ |
|||
|
|||
Checking out 1.29.0 of package ‘swift-protobuf’ |
|||
|
|||
Creating working copy of package ‘GoogleSignIn-iOS’ |
|||
|
|||
Checking out 8.0.0 of package ‘GoogleSignIn-iOS’ |
|||
|
|||
Creating working copy of package ‘DKCamera’ |
|||
|
|||
Checking out master (5c691d1) of package ‘DKCamera’ |
|||
|
|||
Creating working copy of package ‘gtm-session-fetcher’ |
|||
|
|||
Checking out 3.5.0 of package ‘gtm-session-fetcher’ |
|||
|
|||
Creating working copy of package ‘TOCropViewController’ |
|||
|
|||
Checking out 2.7.4 of package ‘TOCropViewController’ |
|||
|
|||
Creating working copy of package ‘DKImagePickerController’ |
|||
|
|||
Checking out 4.3.9 (0bdfeac) of package ‘DKImagePickerController’ |
|||
|
|||
Creating working copy of package ‘GoogleDataTransport’ |
|||
|
|||
Checking out 10.1.0 of package ‘GoogleDataTransport’ |
|||
|
|||
Creating working copy of package ‘swift-openapi-runtime’ |
|||
|
|||
Checking out 1.8.2 of package ‘swift-openapi-runtime’ |
|||
|
|||
Creating working copy of package ‘flutterfire’ |
|||
|
|||
Checking out main (f852df8) of package ‘flutterfire’ |
|||
|
|||
Creating working copy of package ‘SnapKit’ |
|||
|
|||
Checking out 5.7.1 of package ‘SnapKit’ |
|||
|
|||
Creating working copy of package ‘Alamofire’ |
|||
|
|||
Checking out 5.10.2 of package ‘Alamofire’ |
|||
|
|||
Creating working copy of package ‘swift-system’ |
|||
|
|||
Checking out 1.5.0 of package ‘swift-system’ |
|||
|
|||
Creating working copy of package ‘nanopb’ |
|||
|
|||
Checking out 2.30910.0 of package ‘nanopb’ |
|||
|
|||
Creating working copy of package ‘GoogleAppMeasurement’ |
|||
|
|||
Checking out 11.13.0 of package ‘GoogleAppMeasurement’ |
|||
|
|||
Creating working copy of package ‘promises’ |
|||
|
|||
Checking out 2.4.0 of package ‘promises’ |
|||
|
|||
Creating working copy of package ‘SwiftyGif’ |
|||
|
|||
Checking out 5.4.5 of package ‘SwiftyGif’ |
|||
|
|||
Creating working copy of package ‘grpc-binary’ |
|||
|
|||
Checking out 1.69.0 of package ‘grpc-binary’ |
|||
|
|||
Creating working copy of package ‘GoogleUtilities’ |
|||
|
|||
Checking out 8.1.0 of package ‘GoogleUtilities’ |
|||
|
|||
Creating working copy of package ‘OpenAI’ |
|||
|
|||
Checking out 0.4.3 of package ‘OpenAI’ |
|||
|
|||
Creating working copy of package ‘DKPhotoGallery’ |
|||
|
|||
Checking out master (311c1bc) of package ‘DKPhotoGallery’ |
|||
|
|||
Creating working copy of package ‘eventsource’ |
|||
|
|||
Checking out 1.1.1 of package ‘eventsource’ |
|||
|
|||
Creating working copy of package ‘AppAuth-iOS’ |
|||
|
|||
Checking out 1.7.6 of package ‘AppAuth-iOS’ |
|||
|
|||
Creating working copy of package ‘swift-sdk’ |
|||
|
|||
Checking out 0.9.0 of package ‘swift-sdk’ |
|||
|
|||
Creating working copy of package ‘firebase-ios-sdk’ |
|||
|
|||
Checking out 11.13.0 of package ‘firebase-ios-sdk’ |
|||
|
|||
'agent_service' dependency on '/Users/liwei/work/flutter/deepvoice_client/local_plugins/music_service/ios/user_portrait' conflicts with dependency on '/Users/liwei/work/flutter/deepvoice_client/local_plugins/user_portrait/ios/user_portrait' which has the same identity 'user_portrait'. this will be escalated to an error in future versions of SwiftPM. |
|||
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSMutableArray insertObjects:atIndexes:]: count of array (62) differs from count of index set (61)' |
|||
*** First throw call stack: |
|||
( |
|||
0 CoreFoundation 0x0000000188e62ca0 __exceptionPreprocess + 176 |
|||
1 libobjc.A.dylib 0x0000000188926b90 objc_exception_throw + 88 |
|||
2 CoreFoundation 0x0000000188f5d1dc -[__NSCFString characterAtIndex:].cold.1 + 0 |
|||
3 CoreFoundation 0x0000000188e490b8 -[NSMutableArray insertObjects:atIndexes:] + 1444 |
|||
4 IDEFoundation 0x000000010ce72874 __50-[IDEGroup insertGroupSubitems:atIndexes:context:]_block_invoke + 1084 |
|||
5 DVTFoundation 0x000000010b67b16c -[DVTModelObjectGraph performBlockCoalescingModelChanges:] + 60 |
|||
6 IDEFoundation 0x000000010ce72398 -[IDEGroup insertGroupSubitems:atIndexes:context:] + 248 |
|||
7 DVTFoundation 0x000000010b639c30 -[NSMutableArray(DVTFoundationClassAdditions) dvt_sortedInsertOfObjects:withComparator:] + 184 |
|||
8 IDESwiftPackageCore 0x000000011a08c15c $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC32registerDependencyFileReferences33_033DB97E8C2A552F3158921835EE9F98LL_27modelGraphSynchronizerTokenySayAA0abgH9ReferenceO010dependencyU0_AC16Reg 9 IDESwiftPackageCore 0x000000011a08f264 $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC27dependencyPackagesDidUpdate33_033DB97E8C2A552F3158921835EE9F98LL8packages14graphHasErrors25revalidatedFilePathCaches27modelGraphSynchronizerToke 10 IDESwiftPackageCore 0x000000011a08717c $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC27packageGraphDidFinishAction_8duration6resulty7SwiftPM12SPMWorkspaceC0bgJ0O_SdAG0oebgJ16ResolutionResultOtYaFyAC05ModelG17SynchronizerToken33_033 11 IDESwiftPackageCore 0x000000011a08ac18 $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC38disableWorkspaceContentSynchronization33_033DB97E8C2A552F3158921835EE9F98LL6duringyyAC27ModelGraphSynchronizerTokenAELLVXE_tFyyXEfU_TA + 20 |
|||
12 DVTFoundation 0x000000010b84fd68 $sSo19DVTModelObjectGraphC13DVTFoundationE34performBlockCoalescingModelChangesyxxyXElFyyXEfU_ + 104 |
|||
13 DVTFoundation 0x000000010b8134bc $sSDySSSo8NSObjectCGIgo_ACIego_TR + 20 |
|||
14 DVTFoundation 0x000000010b84fa80 $sIeg_IyB_TR + 20 |
|||
15 DVTFoundation 0x000000010b67b24c __58-[DVTModelObjectGraph performBlockCoalescingModelChanges:]_block_invoke + 104 |
|||
16 DVTFoundation 0x000000010b674cf8 -[DVTModelGraphTransactionScope performTransaction:] + 60 |
|||
17 DVTFoundation 0x000000010b67b1c4 -[DVTModelObjectGraph performBlockCoalescingModelChanges:] + 148 |
|||
18 DVTFoundation 0x000000010b850520 $sSo19DVTModelObjectGraphC13DVTFoundationE34performBlockCoalescingModelChangesyxxyXElF + 344 |
|||
19 IDESwiftPackageCore 0x000000011a07d32c $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC38disableWorkspaceContentSynchronization33_033DB97E8C2A552F3158921835EE9F98LL6duringyyAC27ModelGraphSynchronizerTokenAELLVXE_tF + 444 |
|||
20 IDESwiftPackageCore 0x000000011a086198 $s19IDESwiftPackageCore23IDESPMWorkspaceDelegateC27packageGraphDidFinishAction_8duration6resulty7SwiftPM12SPMWorkspaceC0bgJ0O_SdAG0oebgJ16ResolutionResultOtYaFTY4_ + 164 |
|||
21 IDESwiftPackageCore 0x000000011a05ba31 $sIeAgH_ytIeAgHr_TRTATQ0_ + 1 |
|||
22 SwiftPM 0x000000014800cda5 $s7SwiftPM12SPMWorkspaceC26packageGraphActionFinished33_918CA8CFEB8D3E06D2EEA2771EA5EA00LLyyAC07PackageE16ResolutionResultAELLVYaFTQ1_ + 1 |
|||
23 SwiftPM 0x000000014800c6c1 $s7SwiftPM12SPMWorkspaceC46processPackageGraphActionsInBackgroundIfNeeded33_918CA8CFEB8D3E06D2EEA2771EA5EA00LL010canProcessefG0yAC11AffirmativeO_tFyyYaYbcfU1_yyYacfU0_TQ1_ + 1 |
|||
24 SwiftPM 0x000000014805b871 $s7SwiftPM33SPMWorkspaceOrchestrationDelegateP27packageGraphDidFinishAction_8duration6resultyAA0C0C07PackagegJ0O_SdAA0cemgJ16ResolutionResultOtYaFTjTQ0_ + 1 |
|||
25 SwiftPM 0x00000001480d07f1 $sxIeAgHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRyt_Tg5TQ0_ + 1 |
|||
26 SwiftPM 0x0000000148057ff9 $sxIeAgHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRyt_Tg5TATQ0_ + 1 |
|||
27 libswift_Concurrency.dylib 0x0000000271af883d _ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE + 1 |
|||
) |
|||
** INTERNAL ERROR: Uncaught exception ** |
|||
Uncaught Exception: *** -[NSMutableArray insertObjects:atIndexes:]: count of array (62) differs from count of index set (61) |
|||
Stack: |
|||
0 __exceptionPreprocess (in CoreFoundation) |
|||
1 objc_exception_throw (in libobjc.A.dylib) |
|||
2 -[__NSCFString characterAtIndex:].cold.1 (in CoreFoundation) |
|||
3 -[NSMutableArray insertObjects:atIndexes:] (in CoreFoundation) |
|||
4 __50-[IDEGroup insertGroupSubitems:atIndexes:context:]_block_invoke (in IDEFoundation) |
|||
5 -[DVTModelObjectGraph performBlockCoalescingModelChanges:] (in DVTFoundation) |
|||
6 -[IDEGroup insertGroupSubitems:atIndexes:context:] (in IDEFoundation) |
|||
7 -[NSMutableArray(DVTFoundationClassAdditions) dvt_sortedInsertOfObjects:withComparator:] (in DVTFoundation) |
|||
8 specialized IDESPMWorkspaceDelegate.registerDependencyFileReferences(_:modelGraphSynchronizerToken:) (in IDESwiftPackageCore) |
|||
9 specialized IDESPMWorkspaceDelegate.dependencyPackagesDidUpdate(packages:graphHasErrors:revalidatedFilePathCaches:modelGraphSynchronizerToken:) (in IDESwiftPackageCore) |
|||
10 closure #1 in IDESPMWorkspaceDelegate.packageGraphDidFinishAction(_:duration:result:) (in IDESwiftPackageCore) |
|||
11 partial apply for closure #1 in IDESPMWorkspaceDelegate.disableWorkspaceContentSynchronization(during:) (in IDESwiftPackageCore) |
|||
12 <deduplicated_symbol> (in DVTFoundation) |
|||
13 <deduplicated_symbol> (in DVTFoundation) |
|||
14 thunk for @escaping @callee_guaranteed () -> () (in DVTFoundation) |
|||
15 __58-[DVTModelObjectGraph performBlockCoalescingModelChanges:]_block_invoke (in DVTFoundation) |
|||
16 -[DVTModelGraphTransactionScope performTransaction:] (in DVTFoundation) |
|||
17 -[DVTModelObjectGraph performBlockCoalescingModelChanges:] (in DVTFoundation) |
|||
18 DVTModelObjectGraph.performBlockCoalescingModelChanges<A>(_:) (in DVTFoundation) |
|||
19 IDESPMWorkspaceDelegate.disableWorkspaceContentSynchronization(during:) (in IDESwiftPackageCore) |
|||
20 IDESPMWorkspaceDelegate.packageGraphDidFinishAction(_:duration:result:) (in IDESwiftPackageCore) |
|||
21 <deduplicated_symbol> (in IDESwiftPackageCore) |
|||
22 SPMWorkspace.packageGraphActionFinished(_:) (in SwiftPM) |
|||
23 closure #2 in closure #3 in SPMWorkspace.processPackageGraphActionsInBackgroundIfNeeded(canProcessPackageGraphActions:) (in SwiftPM) |
|||
24 <deduplicated_symbol> (in SwiftPM) |
|||
25 specialized thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) (in SwiftPM) |
|||
26 <deduplicated_symbol> (in SwiftPM) |
|||
27 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) (in libswift_Concurrency.dylib) |
|||
Command: /usr/bin/arch -arm64e xcrun xcodebuild -list |
|||
|
|||
``` |
|||
#0 RunResult.throwException (package:flutter_tools/src/base/process.dart:118:5) |
|||
#1 _DefaultProcessUtils.run (package:flutter_tools/src/base/process.dart:344:19) |
|||
<asynchronous suspension> |
|||
#2 XcodeProjectInterpreter.getInfo (package:flutter_tools/src/ios/xcodeproj.dart:334:30) |
|||
<asynchronous suspension> |
|||
#3 XcodeBasedProject.projectInfo (package:flutter_tools/src/xcode_project.dart:197:29) |
|||
<asynchronous suspension> |
|||
#4 XcodeBasedProject.buildSettingsForBuildInfo (package:flutter_tools/src/xcode_project.dart:217:36) |
|||
<asynchronous suspension> |
|||
#5 IosProject._parseProductName (package:flutter_tools/src/xcode_project.dart:647:55) |
|||
<asynchronous suspension> |
|||
#6 IosProject.productName (package:flutter_tools/src/xcode_project.dart:635:29) |
|||
<asynchronous suspension> |
|||
#7 BuildableIOSApp.fromProject (package:flutter_tools/src/ios/application_package.dart:117:33) |
|||
<asynchronous suspension> |
|||
#8 FlutterApplicationPackageFactory.getPackageForPlatform (package:flutter_tools/src/flutter_application_package.dart:80:15) |
|||
<asynchronous suspension> |
|||
#9 _BuildIOSSubCommand.buildableIOSApp.<anonymous closure> (package:flutter_tools/src/commands/build_ios.dart:707:9) |
|||
<asynchronous suspension> |
|||
#10 _BuildIOSSubCommand.runCommand (package:flutter_tools/src/commands/build_ios.dart:748:33) |
|||
<asynchronous suspension> |
|||
#11 FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1563:27) |
|||
<asynchronous suspension> |
|||
#12 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:154:19) |
|||
<asynchronous suspension> |
|||
#13 CommandRunner.runCommand (package:args/command_runner.dart:212:13) |
|||
<asynchronous suspension> |
|||
#14 FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:494:9) |
|||
<asynchronous suspension> |
|||
#15 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:154:19) |
|||
<asynchronous suspension> |
|||
#16 FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:431:5) |
|||
<asynchronous suspension> |
|||
#17 run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:98:11) |
|||
<asynchronous suspension> |
|||
#18 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:154:19) |
|||
<asynchronous suspension> |
|||
#19 main (package:flutter_tools/executable.dart:102:3) |
|||
<asynchronous suspension> |
|||
``` |
|||
|
|||
## flutter doctor |
|||
|
|||
``` |
|||
[32m[✓][39m Flutter (Channel stable, 3.32.3, on macOS 15.5 24F74 darwin-arm64, locale zh-Hans-CN) [429ms] |
|||
[32m•[39m Flutter version 3.32.3 on channel stable at /Users/liwei/flutter |
|||
[32m•[39m Upstream repository https://github.com/flutter/flutter.git |
|||
[32m•[39m Framework revision 5c1433509f (9 weeks ago), 2025-06-11 13:12:24 -0700 |
|||
[32m•[39m Engine revision 31c4875c7a |
|||
[32m•[39m Dart version 3.8.1 |
|||
[32m•[39m DevTools version 2.45.1 |
|||
|
|||
[33m[!][39m Android toolchain - develop for Android devices (Android SDK version 36.0.0) [2.8s] |
|||
[32m•[39m Android SDK at /Users/liwei/Library/Android/sdk |
|||
[32m•[39m Platform android-36, build-tools 36.0.0 |
|||
[32m•[39m Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java |
|||
This is the JDK bundled with the latest Android Studio installation on this machine. |
|||
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`. |
|||
[32m•[39m Java version OpenJDK Runtime Environment (build 21.0.4+-12422083-b607.1) |
|||
[33m![39m Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses |
|||
|
|||
[32m[✓][39m Xcode - develop for iOS and macOS (Xcode 16.2) [1,682ms] |
|||
[32m•[39m Xcode at /Applications/Xcode.app/Contents/Developer |
|||
[32m•[39m Build 16C5032a |
|||
[32m•[39m CocoaPods version 1.16.2 |
|||
|
|||
[32m[✓][39m Chrome - develop for the web [6ms] |
|||
[32m•[39m Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome |
|||
|
|||
[32m[✓][39m Android Studio (version 2024.2) [5ms] |
|||
[32m•[39m Android Studio at /Applications/Android Studio.app/Contents |
|||
[32m•[39m Flutter plugin can be installed from: |
|||
🔨 https://plugins.jetbrains.com/plugin/9212-flutter |
|||
[32m•[39m Dart plugin can be installed from: |
|||
🔨 https://plugins.jetbrains.com/plugin/6351-dart |
|||
[32m•[39m Java version OpenJDK Runtime Environment (build 21.0.4+-12422083-b607.1) |
|||
|
|||
[32m[✓][39m VS Code (version 1.103.0) [4ms] |
|||
[32m•[39m VS Code at /Applications/Visual Studio Code.app/Contents |
|||
[32m•[39m Flutter extension version 3.116.0 |
|||
|
|||
[32m[✓][39m Connected device (5 available) [9.4s] |
|||
[32m•[39m RMX3310 (mobile) • a709b98b • android-arm64 • Android 14 (API 34) |
|||
[32m•[39m Voitrans的iPhoneXr (wireless) (mobile) • 00008020-000175D23C03002E • ios • iOS 18.6 22G86 |
|||
[32m•[39m iPhone XR (wireless) (mobile) • 00008020-000140140260802E • ios • iOS 18.5 22F76 |
|||
[32m•[39m macOS (desktop) • macos • darwin-arm64 • macOS 15.5 24F74 darwin-arm64 |
|||
[32m•[39m Chrome (web) • chrome • web-javascript • Google Chrome 139.0.7258.67 |
|||
[33m![39m Error: Browsing on the local area network for 李伟的iPad. Ensure the device is unlocked and attached with a cable or |
|||
associated with the same local area network as this Mac. |
|||
The device must be opted into Developer Mode to connect wirelessly. (code -27) |
|||
[33m![39m Error: Browsing on the local area network for liwei. Ensure the device is unlocked and attached with a cable or |
|||
associated with the same local area network as this Mac. |
|||
The device must be opted into Developer Mode to connect wirelessly. (code -27) |
|||
|
|||
[32m[✓][39m Network resources [5.1s] |
|||
[32m•[39m All expected network resources are available. |
|||
|
|||
[33m![39m Doctor found issues in 1 category. |
|||
``` |
|||
@ -0,0 +1,47 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>AvailableLibraries</key> |
|||
<array> |
|||
<dict> |
|||
<key>BinaryPath</key> |
|||
<string>libQPlayAutoSDK-x86_64.a</string> |
|||
<key>HeadersPath</key> |
|||
<string>Headers</string> |
|||
<key>LibraryIdentifier</key> |
|||
<string>ios-x86_64-simulator</string> |
|||
<key>LibraryPath</key> |
|||
<string>libQPlayAutoSDK-x86_64.a</string> |
|||
<key>SupportedArchitectures</key> |
|||
<array> |
|||
<string>x86_64</string> |
|||
</array> |
|||
<key>SupportedPlatform</key> |
|||
<string>ios</string> |
|||
<key>SupportedPlatformVariant</key> |
|||
<string>simulator</string> |
|||
</dict> |
|||
<dict> |
|||
<key>BinaryPath</key> |
|||
<string>libQPlayAutoSDK-arm64.a</string> |
|||
<key>HeadersPath</key> |
|||
<string>Headers</string> |
|||
<key>LibraryIdentifier</key> |
|||
<string>ios-arm64</string> |
|||
<key>LibraryPath</key> |
|||
<string>libQPlayAutoSDK-arm64.a</string> |
|||
<key>SupportedArchitectures</key> |
|||
<array> |
|||
<string>arm64</string> |
|||
</array> |
|||
<key>SupportedPlatform</key> |
|||
<string>ios</string> |
|||
</dict> |
|||
</array> |
|||
<key>CFBundlePackageType</key> |
|||
<string>XFWK</string> |
|||
<key>XCFrameworkFormatVersion</key> |
|||
<string>1.0</string> |
|||
</dict> |
|||
</plist> |
|||
@ -0,0 +1,7 @@ |
|||
//
|
|||
// QPlayAutoSDK.h
|
|||
// QPlayAutoSDK Umbrella Header
|
|||
//
|
|||
|
|||
#import "include/QPlayAutoSDK.h" |
|||
#import "include/QPlayAutoDefine.h" |
|||
@ -0,0 +1,5 @@ |
|||
framework module QPlayAutoSDK { |
|||
umbrella header "QPlayAutoSDK.h" |
|||
export * |
|||
module * { export * } |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,7 @@ |
|||
//
|
|||
// QPlayAutoSDK.h
|
|||
// QPlayAutoSDK Umbrella Header
|
|||
//
|
|||
|
|||
#import "include/QPlayAutoSDK.h" |
|||
#import "include/QPlayAutoDefine.h" |
|||
@ -0,0 +1,238 @@ |
|||
//
|
|||
// QPlayAutoDefine.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/17.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#ifndef QPlayAutoDefine_h |
|||
#define QPlayAutoDefine_h |
|||
|
|||
extern NSString *const kQPlayAutoItemRootID; |
|||
extern NSString *const kQPlayAutoArgument_Count; |
|||
extern NSString *const kQPlayAutoArgument_PageIndex; |
|||
extern NSString *const kQPlayAutoArgument_ParentID; |
|||
extern NSString *const kQPlayAutoArgument_Lists; |
|||
extern NSString *const kQPlayAutoArgument_PlayMode; |
|||
extern NSString *const kQPlayAutoArgument_IsFav; |
|||
extern NSString *const kQPlayAutoArgument_Position; |
|||
extern NSString *const kQPlayAutoArgument_Song; |
|||
extern NSString *const kQPlayAutoArgument_SongID; |
|||
extern NSString *const kQPlayAutoArgument_State; |
|||
extern NSString *const kQPlayAutoArgument_Error; |
|||
|
|||
/*
|
|||
QPlayAuto错误 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoError) |
|||
{ |
|||
QPlayAuto_OK=0, //成功
|
|||
QPlayAuto_QueryAutoFailed=100, //查询车机信息失败
|
|||
QPlayAuto_QueryMobileFailed=101, //查询移动设备失败
|
|||
QPlayAuto_QuerySongsFailed_WithoutID=102, //查询歌单数据失败,父 ID 不存在
|
|||
QPlayAuto_QuerySongsFailed_NoNetwork=103, //查询歌单数据失败,移动设备网络不通,无法下载数据
|
|||
QPlayAuto_QuerySongsFailed_Unknow=104, //查询歌单数据失败,原因未知
|
|||
QPlayAuto_SongIDError=105, //歌曲 ID 不存在
|
|||
QPlayAuto_ReadError=106, //读取数据错误
|
|||
QPlayAuto_ParamError=107, //参数错误
|
|||
QPlayAuto_SystemError=108, //系统调用错误
|
|||
QPlayAuto_CopyRightError=109, //无法播放,没有版权
|
|||
QPlayAuto_LoginError=110, //无法读取,没有登录
|
|||
QPlayAuto_PcmRepeat=111, //PCM 请求重复
|
|||
QPlayAuto_NoPermission=112, //没有权限
|
|||
QPlayAuto_NeedBuy = 113, //无法播放(数字专辑)
|
|||
QPlayAuto_NotSupportFormat=114, //无法播放(不支持格式)
|
|||
QPlayAuto_NeedAuth=115, //需要授权
|
|||
QPlayAuto_NeedVIP = 116, //无法播放(需要购买VIP)
|
|||
QPlayAuto_TryListen = 117, //试听歌曲,购买听完整版
|
|||
QPlayAuto_TrafficAlert = 118, //无法播放(流量弹窗阻断)
|
|||
QPlayAuto_OnlyWifi = 119, //无法播放(仅Wifi弹窗阻断)
|
|||
QPlayAuto_AlreadyConnected=120, //已经连接车机
|
|||
QPlayAuto_SeekError = 121, //拖拽进度错误
|
|||
QPlayAuto_NoListenRight = 122, //无法播放(无版权)
|
|||
QPlayAuto_SongNotMatch=123, //歌曲不匹配
|
|||
QPlayAuto_Disconnect=124, //连接已断开
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto连接状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoConnectState) |
|||
{ |
|||
QPlayAutoConnectState_Connected = 0, //连接成功
|
|||
QPlayAutoConnectState_Disconnect = 1, //断开连接
|
|||
QPlayAutoConnectState_Failed = -1, //连接失败
|
|||
QPlayAutoConnectState_Cancel = -2, //连接取消
|
|||
}; |
|||
|
|||
/*
|
|||
QPlayAutoListItem类型 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoListItemType) |
|||
{ |
|||
QPlayAutoListItemType_Song = 1, //歌曲
|
|||
QPlayAutoListItemType_Normal = 2, //普通目录
|
|||
QPlayAutoListItemType_Radio = 3, //电台
|
|||
QPlayAutoListItemType_Folder = 4, //歌单
|
|||
QPlayAutoListItemType_Album = 5, //专辑
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSUInteger, QPlayAutoSongListType) |
|||
{ |
|||
QPlayAutoSongListType_Daily = 108, //每日30首
|
|||
}; |
|||
|
|||
|
|||
/**
|
|||
QPlayAuto播放状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayState) |
|||
{ |
|||
QPlayAutoPlayState_Stop=0, //停止
|
|||
QPlayAutoPlayState_Playing=1, //播放
|
|||
QPlayAutoPlayState_Pause=2 //暂停
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSInteger, QPlayAutoAssenceMode) |
|||
{ |
|||
QPlayAutoAssenceMode_Full = 0, // 默认整首播放
|
|||
QPlayAutoAssenceMode_Part = 1, // 仅播放精华片段
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto播放模式 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayMode) |
|||
{ |
|||
QPlayAutoPlayMode_SequenceCircle=0, //列表循环
|
|||
QPlayAutoPlayMode_SingleCircle=1, //单曲循环
|
|||
QPlayAutoPlayMode_RandomCircle=2 //随机播放
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto搜索类型 |
|||
*/ |
|||
typedef NS_ENUM(NSInteger, QPlayAutoSearchType) |
|||
{ |
|||
QPlayAutoSearchType_Song = 0, //歌曲
|
|||
QPlayAutoSearchType_Album = 2, //专辑
|
|||
QPlayAutoSearchType_Folder = 3, //歌单
|
|||
QPlayAutoSearchType_Composite = 100, //综合
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSInteger, QPlayAutoVipState) |
|||
{ |
|||
QPlayAutoVipState_Normal = 0, |
|||
QPlayAutoVipState_VIP = 1, |
|||
QPlayAutoVipState_SVIP = 2, |
|||
}; |
|||
|
|||
/**
|
|||
第三方App信息 |
|||
*/ |
|||
@interface QPlayAutoAppInfo : NSObject<NSCoding> |
|||
|
|||
@property (nonatomic,strong) NSString *deviceId; //AppId
|
|||
@property (nonatomic,assign) NSInteger deviceType; |
|||
@property (nonatomic,strong) NSString *scheme; //跳转scheme
|
|||
@property (nonatomic,strong) NSString *brand; //品牌
|
|||
@property (nonatomic,strong) NSString *name; //名称
|
|||
@property (nonatomic,strong) NSString *appId; //名称
|
|||
@property (nonatomic,strong) NSString *secretKey; //私钥
|
|||
@property (nonatomic,strong) NSString *bundleId; //bundleId
|
|||
@property (nonatomic,assign) NSInteger qmCommandPort; //Q音接收命令端口
|
|||
@property (nonatomic,strong) NSString *qmHost; //Q音接收命令IP
|
|||
@property (nonatomic,strong) NSDate *lastConnectDate;//上次连接的日期
|
|||
@end |
|||
|
|||
@interface QPlayAutoSinger : NSObject |
|||
@property (nonatomic,strong) NSString *ID; //ID
|
|||
@property (nonatomic,strong) NSString *Mid; //Mid
|
|||
@property (nonatomic,strong) NSString *name; //名称
|
|||
@end |
|||
|
|||
|
|||
/*
|
|||
QPlayAuto列表项(歌单、电台、排行榜、歌曲、etc) |
|||
*/ |
|||
@interface QPlayAutoListItem: NSObject |
|||
|
|||
@property (nonatomic,strong) NSString *ID; //ID
|
|||
@property (nonatomic,strong) NSString *Name; //名称
|
|||
@property (nonatomic,strong) NSString *SubName; //副标题
|
|||
@property (nonatomic,strong) QPlayAutoSinger *singer; //歌手
|
|||
@property (nonatomic,strong) NSString *Album; //专辑
|
|||
@property (nonatomic,strong) NSString *Mid; |
|||
@property (nonatomic,assign) QPlayAutoVipState vipState; |
|||
@property (nonatomic,assign) QPlayAutoListItemType Type; |
|||
@property (nonatomic,assign) NSInteger Duration; //时长
|
|||
@property (nonatomic,strong) NSString *CoverUri; //封面Uri
|
|||
@property (nonatomic,assign) NSInteger totalCount; //总数
|
|||
@property (nonatomic,assign) BOOL isFav; //是否收藏
|
|||
@property (nonatomic,assign) BOOL isOrigin; //是否原唱
|
|||
@property (nonatomic,assign) BOOL isNoAudioSource; //无音源
|
|||
@property (nonatomic,strong) NSMutableArray<QPlayAutoListItem*> *items; //子列表
|
|||
@property (nonatomic,weak) QPlayAutoListItem *parentItem; //父节点
|
|||
|
|||
@property (nonatomic,assign) BOOL isTryListen; //是否试听歌曲
|
|||
@property (nonatomic,readonly) BOOL isSong; //歌曲
|
|||
@property (nonatomic,readonly) BOOL isFolder;//歌单
|
|||
@property (nonatomic,readonly) BOOL isAlbum; //专辑
|
|||
@property (nonatomic,readonly) BOOL isRadio; //电台
|
|||
@property (nonatomic,readonly) BOOL isDirectory; //普通目录
|
|||
|
|||
@property (nonatomic,readonly) BOOL isVIP; //VIP歌曲
|
|||
@property (nonatomic,readonly) BOOL isSVIP; //SVIP歌曲
|
|||
|
|||
- (instancetype)initWithDictionary:(NSDictionary*)dict; |
|||
- (instancetype)initSongWithId:(NSString *)identifier isFav:(BOOL)isFav; |
|||
- (instancetype)initWithId:(NSString *)identifier type:(QPlayAutoListItemType)type; |
|||
|
|||
//是否是根节点
|
|||
- (BOOL)isRoot; |
|||
|
|||
//是否有更多
|
|||
- (BOOL)hasMore; |
|||
|
|||
- (QPlayAutoListItem*)findItemWithID:(NSString*)ID; |
|||
|
|||
@end |
|||
|
|||
/**
|
|||
QPlayAuto请求回调 |
|||
|
|||
@param success 是否成功 |
|||
@param dict 结果Dictionary |
|||
*/ |
|||
typedef void (^QPlayAutoRequestFinishBlock)(BOOL success, NSDictionary *dict); |
|||
|
|||
|
|||
@interface QPlayAutoRequestBase : NSObject |
|||
|
|||
/** 请求类型 */ |
|||
@property (nonatomic, assign) int type; |
|||
|
|||
@end |
|||
|
|||
@interface QPlayAutoSentence : NSObject <NSCoding> |
|||
@property (nonatomic, assign) NSInteger startTime; |
|||
@property (nonatomic, assign) NSInteger duration; |
|||
@property (nonatomic, strong) NSString *text; |
|||
@property (nonatomic, readonly) NSString *startTimeText; |
|||
@property (nonatomic, readonly) NSString *endTimeText; |
|||
@end |
|||
|
|||
@interface QPlayAutoLyric : NSObject <NSCoding> |
|||
@property(nonatomic,assign) BOOL isTxtType;//歌词是不是纯文本的歌词,没有时间标记
|
|||
@property(nonatomic,strong) NSString *songId; |
|||
@property(nonatomic,strong) NSString *songName; |
|||
@property(nonatomic,strong) NSString *singerName; |
|||
@property(nonatomic,strong) NSString *albumName; |
|||
@property(nonatomic,readonly) NSString *text;//所有歌词文本
|
|||
@property(nonatomic,strong) NSArray<QPlayAutoSentence *> *sentences; |
|||
- (NSString *)sentenceAtTime:(NSTimeInterval)time; |
|||
@end |
|||
|
|||
|
|||
#endif /* QPlayAutoDefine_h */ |
|||
@ -0,0 +1,188 @@ |
|||
//
|
|||
// QPlayAutoSDK.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/5.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#import <Foundation/Foundation.h> |
|||
#import "QPlayAutoDefine.h" |
|||
|
|||
#define APP_DEVICE_TYPE (3) |
|||
|
|||
NS_ASSUME_NONNULL_BEGIN |
|||
|
|||
typedef void (^QPlayAutoResultItemsBlock)(NSInteger errorCode,NSArray<QPlayAutoListItem *> *_Nullable items); |
|||
typedef void (^QPlayAutoResultParentItemBlock)(NSInteger errorCode,QPlayAutoListItem *parentItem); |
|||
|
|||
extern NSString const * QPlayAutoSDKVersion; |
|||
|
|||
@protocol QPlayAutoSDKDelegate <NSObject> |
|||
|
|||
//连接状态变化回调
|
|||
- (void)onQPlayAutoConnectStateChanged:(QPlayAutoConnectState)newState; |
|||
//播放状态状态变化回调
|
|||
- (void)onQPlayAutoPlayStateChanged:(QPlayAutoPlayState)playState song:(QPlayAutoListItem *)song position:(NSInteger)position; |
|||
//歌曲播放进度
|
|||
- (void)onQPlayAutoPlayProgressChanged:(QPlayAutoListItem *)song progress:(NSTimeInterval)progress duration:(NSTimeInterval)duration; |
|||
//歌曲收藏状态变化
|
|||
- (void)onSongFavoriteStateChange:(NSString*)songID isFavorite:(BOOL)isFavorite; |
|||
//播放状态事件变化
|
|||
- (void)onPlayModeChange:(QPlayAutoPlayMode)playMode; |
|||
//定时关闭事件
|
|||
- (void)onPlayPausedByTimeoff; |
|||
//登陆状态
|
|||
- (void)onLoginStateDidChanged:(BOOL)isLoginOK; |
|||
@end |
|||
|
|||
|
|||
|
|||
@interface QPlayAutoSDK : NSObject |
|||
|
|||
/// 注册App
|
|||
/// @param name 名称
|
|||
/// @param brand 品牌
|
|||
/// @param deviceId ?
|
|||
/// @param scheme 授权成功后Q音跳转合作方的scheme
|
|||
/// @param appId Q音分配的Id
|
|||
/// @param secretKey 合作方私钥
|
|||
/// @param bundleId 合作方包名
|
|||
+ (void)registerAppWithName:(NSString *)name brand:(NSString *)brand deviceId:(NSString *)deviceId scheme:(NSString *)scheme appId:(NSString *)appId secretKey:(NSString *)secretKey bundleId:(NSString *)bundleId; |
|||
|
|||
/// 播放状态
|
|||
+ (QPlayAutoPlayMode)currentPlayMode; |
|||
/// 当前歌曲
|
|||
+ (QPlayAutoListItem *_Nullable)currentSong; |
|||
/// 当前播放状态
|
|||
+ (QPlayAutoPlayState)currentPlayState; |
|||
/// 当前播放进度
|
|||
+ (NSTimeInterval)currentProgress; |
|||
/// QQ音乐是否登录
|
|||
+ (BOOL)isLoginOK; |
|||
/// 检查QQ音乐是否已安装
|
|||
+ (BOOL)isQQMusicInstalled; |
|||
/// 是否已连接
|
|||
+ (BOOL)isConnected; |
|||
/// 连接中
|
|||
+ (BOOL)isConnecting; |
|||
///QQ音乐设备信息
|
|||
+ (NSDictionary *)deviceInfo; |
|||
|
|||
+(NSString *)openId; |
|||
+(NSString *)openToken; |
|||
|
|||
|
|||
///是否可以处理回调url
|
|||
+ (BOOL)canHandleOpenURL:(NSURL *)url; |
|||
///处理回调url
|
|||
+ (BOOL)handleOpenURL:(NSURL *)url; |
|||
|
|||
/// 回调代理
|
|||
+ (void)setDelegate:(id<QPlayAutoSDKDelegate>) delegate; |
|||
|
|||
|
|||
/// 拉端连接QPlay
|
|||
/// - Parameter forceLogin: 是否强制登录
|
|||
+ (void)connectAndForceLogin:(BOOL)forceLogin; |
|||
/// 停止
|
|||
+ (void)stop; |
|||
|
|||
|
|||
/// 二次热启动连接(需要联系Q音打开此功能才能使用)
|
|||
/// - Parameters:
|
|||
/// - timeout: 超时时间
|
|||
/// - completion: 回调
|
|||
+ (void)reconnectWithTimeout:(NSTimeInterval)timeout completion:(QPlayAutoRequestFinishBlock)completion; |
|||
|
|||
/// 跳转QQ音乐登录
|
|||
/// @param bundleId 合作方bundleId
|
|||
/// @param callbackUrl 跳转合作方url
|
|||
+ (BOOL)loginQQMusicWithBundleId:(NSString *)bundleId callbackUrl:(NSString *)callbackUrl; |
|||
|
|||
|
|||
/// 获取数据
|
|||
/// @param parent 父
|
|||
/// @param pageIndex 页码
|
|||
/// @param pageSize 页大小
|
|||
/// @param completion 回调
|
|||
+ (void)getDataItemsFromParent:(QPlayAutoListItem *)parent pageIndex:(NSUInteger)pageIndex pageSize:(NSUInteger)pageSize completion:(QPlayAutoResultParentItemBlock)completion; |
|||
|
|||
|
|||
/// 设置播放模式
|
|||
/// @param playMode 播放模式
|
|||
/// @param block 回调
|
|||
+ (NSInteger)setPlayMode:(QPlayAutoPlayMode)playMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; |
|||
|
|||
|
|||
/// 设置播放整首还是高潮
|
|||
/// @param assencceMode 高潮还是整首
|
|||
/// @param block 回调
|
|||
+ (NSInteger)setAssenceMode:(QPlayAutoAssenceMode)assencceMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; |
|||
|
|||
/// 收藏/取消收藏(歌曲id)
|
|||
/// @param song 歌曲
|
|||
/// @param isFavorite 收藏/取消收藏
|
|||
/// @param completion 回调
|
|||
+ (void)setFavoriteStateWithSong:(QPlayAutoListItem *)song isFavorite:(BOOL)isFavorite completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
/// 播放歌曲
|
|||
/// @param songList 歌曲列表
|
|||
/// @param playIndex 播放索引
|
|||
/// @param completion 回调
|
|||
+ (void)playAtIndex:(NSArray<QPlayAutoListItem*> *)songList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
|
|||
/// 通过歌曲Mid列表播放歌曲
|
|||
/// @param songMidList 歌曲Mid列表
|
|||
/// @param playIndex 播放索引
|
|||
/// @param completion 回调
|
|||
+ (void)playSongMidAtIndex:(NSArray<NSString*> *)songMidList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
|
|||
///播放下一首
|
|||
+ (void)playerPlayNextWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///播放上一首
|
|||
+ (void)playerPlayPrevWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///播放暂停
|
|||
+ (void)playerPlayPause; |
|||
|
|||
///播放恢复
|
|||
+ (void)playerPlayResumeWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///Seek到指定位置
|
|||
+ (void)playerSeek:(NSInteger)position; |
|||
|
|||
|
|||
/// 搜索歌曲
|
|||
/// @param keyword 关键词
|
|||
/// @param type 搜索类型
|
|||
/// @param firstPage 是否第一页
|
|||
/// @param completion 回调
|
|||
+ (NSInteger)search:(NSString *)keyword |
|||
type:(QPlayAutoSearchType)type |
|||
firstPage:(BOOL)firstPage |
|||
completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
/// 相似歌曲
|
|||
/// @param song 歌曲
|
|||
/// @param completion 回调
|
|||
+ (void)requestSimilarWithSong:(QPlayAutoListItem *)song completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
|
|||
/// 查询歌词
|
|||
/// @param song 歌曲
|
|||
/// @param completion 回调
|
|||
+ (void)requestLyricWithSong:(QPlayAutoListItem *)song completion:(void (^)(NSInteger errorCode,QPlayAutoLyric *_Nullable lyric))completion; |
|||
|
|||
|
|||
/// 获取特定歌曲列表
|
|||
/// @param type 列表类型
|
|||
/// @param completion 回调
|
|||
+ (void)requestSonglistWithType:(QPlayAutoSongListType)type completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
@end |
|||
|
|||
NS_ASSUME_NONNULL_END |
|||
@ -0,0 +1,5 @@ |
|||
framework module QPlayAutoSDK { |
|||
umbrella header "QPlayAutoSDK.h" |
|||
export * |
|||
module * { export * } |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,238 @@ |
|||
//
|
|||
// QPlayAutoDefine.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/17.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#ifndef QPlayAutoDefine_h |
|||
#define QPlayAutoDefine_h |
|||
|
|||
extern NSString *const kQPlayAutoItemRootID; |
|||
extern NSString *const kQPlayAutoArgument_Count; |
|||
extern NSString *const kQPlayAutoArgument_PageIndex; |
|||
extern NSString *const kQPlayAutoArgument_ParentID; |
|||
extern NSString *const kQPlayAutoArgument_Lists; |
|||
extern NSString *const kQPlayAutoArgument_PlayMode; |
|||
extern NSString *const kQPlayAutoArgument_IsFav; |
|||
extern NSString *const kQPlayAutoArgument_Position; |
|||
extern NSString *const kQPlayAutoArgument_Song; |
|||
extern NSString *const kQPlayAutoArgument_SongID; |
|||
extern NSString *const kQPlayAutoArgument_State; |
|||
extern NSString *const kQPlayAutoArgument_Error; |
|||
|
|||
/*
|
|||
QPlayAuto错误 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoError) |
|||
{ |
|||
QPlayAuto_OK=0, //成功
|
|||
QPlayAuto_QueryAutoFailed=100, //查询车机信息失败
|
|||
QPlayAuto_QueryMobileFailed=101, //查询移动设备失败
|
|||
QPlayAuto_QuerySongsFailed_WithoutID=102, //查询歌单数据失败,父 ID 不存在
|
|||
QPlayAuto_QuerySongsFailed_NoNetwork=103, //查询歌单数据失败,移动设备网络不通,无法下载数据
|
|||
QPlayAuto_QuerySongsFailed_Unknow=104, //查询歌单数据失败,原因未知
|
|||
QPlayAuto_SongIDError=105, //歌曲 ID 不存在
|
|||
QPlayAuto_ReadError=106, //读取数据错误
|
|||
QPlayAuto_ParamError=107, //参数错误
|
|||
QPlayAuto_SystemError=108, //系统调用错误
|
|||
QPlayAuto_CopyRightError=109, //无法播放,没有版权
|
|||
QPlayAuto_LoginError=110, //无法读取,没有登录
|
|||
QPlayAuto_PcmRepeat=111, //PCM 请求重复
|
|||
QPlayAuto_NoPermission=112, //没有权限
|
|||
QPlayAuto_NeedBuy = 113, //无法播放(数字专辑)
|
|||
QPlayAuto_NotSupportFormat=114, //无法播放(不支持格式)
|
|||
QPlayAuto_NeedAuth=115, //需要授权
|
|||
QPlayAuto_NeedVIP = 116, //无法播放(需要购买VIP)
|
|||
QPlayAuto_TryListen = 117, //试听歌曲,购买听完整版
|
|||
QPlayAuto_TrafficAlert = 118, //无法播放(流量弹窗阻断)
|
|||
QPlayAuto_OnlyWifi = 119, //无法播放(仅Wifi弹窗阻断)
|
|||
QPlayAuto_AlreadyConnected=120, //已经连接车机
|
|||
QPlayAuto_SeekError = 121, //拖拽进度错误
|
|||
QPlayAuto_NoListenRight = 122, //无法播放(无版权)
|
|||
QPlayAuto_SongNotMatch=123, //歌曲不匹配
|
|||
QPlayAuto_Disconnect=124, //连接已断开
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto连接状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoConnectState) |
|||
{ |
|||
QPlayAutoConnectState_Connected = 0, //连接成功
|
|||
QPlayAutoConnectState_Disconnect = 1, //断开连接
|
|||
QPlayAutoConnectState_Failed = -1, //连接失败
|
|||
QPlayAutoConnectState_Cancel = -2, //连接取消
|
|||
}; |
|||
|
|||
/*
|
|||
QPlayAutoListItem类型 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoListItemType) |
|||
{ |
|||
QPlayAutoListItemType_Song = 1, //歌曲
|
|||
QPlayAutoListItemType_Normal = 2, //普通目录
|
|||
QPlayAutoListItemType_Radio = 3, //电台
|
|||
QPlayAutoListItemType_Folder = 4, //歌单
|
|||
QPlayAutoListItemType_Album = 5, //专辑
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSUInteger, QPlayAutoSongListType) |
|||
{ |
|||
QPlayAutoSongListType_Daily = 108, //每日30首
|
|||
}; |
|||
|
|||
|
|||
/**
|
|||
QPlayAuto播放状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayState) |
|||
{ |
|||
QPlayAutoPlayState_Stop=0, //停止
|
|||
QPlayAutoPlayState_Playing=1, //播放
|
|||
QPlayAutoPlayState_Pause=2 //暂停
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSInteger, QPlayAutoAssenceMode) |
|||
{ |
|||
QPlayAutoAssenceMode_Full = 0, // 默认整首播放
|
|||
QPlayAutoAssenceMode_Part = 1, // 仅播放精华片段
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto播放模式 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayMode) |
|||
{ |
|||
QPlayAutoPlayMode_SequenceCircle=0, //列表循环
|
|||
QPlayAutoPlayMode_SingleCircle=1, //单曲循环
|
|||
QPlayAutoPlayMode_RandomCircle=2 //随机播放
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto搜索类型 |
|||
*/ |
|||
typedef NS_ENUM(NSInteger, QPlayAutoSearchType) |
|||
{ |
|||
QPlayAutoSearchType_Song = 0, //歌曲
|
|||
QPlayAutoSearchType_Album = 2, //专辑
|
|||
QPlayAutoSearchType_Folder = 3, //歌单
|
|||
QPlayAutoSearchType_Composite = 100, //综合
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSInteger, QPlayAutoVipState) |
|||
{ |
|||
QPlayAutoVipState_Normal = 0, |
|||
QPlayAutoVipState_VIP = 1, |
|||
QPlayAutoVipState_SVIP = 2, |
|||
}; |
|||
|
|||
/**
|
|||
第三方App信息 |
|||
*/ |
|||
@interface QPlayAutoAppInfo : NSObject<NSCoding> |
|||
|
|||
@property (nonatomic,strong) NSString *deviceId; //AppId
|
|||
@property (nonatomic,assign) NSInteger deviceType; |
|||
@property (nonatomic,strong) NSString *scheme; //跳转scheme
|
|||
@property (nonatomic,strong) NSString *brand; //品牌
|
|||
@property (nonatomic,strong) NSString *name; //名称
|
|||
@property (nonatomic,strong) NSString *appId; //名称
|
|||
@property (nonatomic,strong) NSString *secretKey; //私钥
|
|||
@property (nonatomic,strong) NSString *bundleId; //bundleId
|
|||
@property (nonatomic,assign) NSInteger qmCommandPort; //Q音接收命令端口
|
|||
@property (nonatomic,strong) NSString *qmHost; //Q音接收命令IP
|
|||
@property (nonatomic,strong) NSDate *lastConnectDate;//上次连接的日期
|
|||
@end |
|||
|
|||
@interface QPlayAutoSinger : NSObject |
|||
@property (nonatomic,strong) NSString *ID; //ID
|
|||
@property (nonatomic,strong) NSString *Mid; //Mid
|
|||
@property (nonatomic,strong) NSString *name; //名称
|
|||
@end |
|||
|
|||
|
|||
/*
|
|||
QPlayAuto列表项(歌单、电台、排行榜、歌曲、etc) |
|||
*/ |
|||
@interface QPlayAutoListItem: NSObject |
|||
|
|||
@property (nonatomic,strong) NSString *ID; //ID
|
|||
@property (nonatomic,strong) NSString *Name; //名称
|
|||
@property (nonatomic,strong) NSString *SubName; //副标题
|
|||
@property (nonatomic,strong) QPlayAutoSinger *singer; //歌手
|
|||
@property (nonatomic,strong) NSString *Album; //专辑
|
|||
@property (nonatomic,strong) NSString *Mid; |
|||
@property (nonatomic,assign) QPlayAutoVipState vipState; |
|||
@property (nonatomic,assign) QPlayAutoListItemType Type; |
|||
@property (nonatomic,assign) NSInteger Duration; //时长
|
|||
@property (nonatomic,strong) NSString *CoverUri; //封面Uri
|
|||
@property (nonatomic,assign) NSInteger totalCount; //总数
|
|||
@property (nonatomic,assign) BOOL isFav; //是否收藏
|
|||
@property (nonatomic,assign) BOOL isOrigin; //是否原唱
|
|||
@property (nonatomic,assign) BOOL isNoAudioSource; //无音源
|
|||
@property (nonatomic,strong) NSMutableArray<QPlayAutoListItem*> *items; //子列表
|
|||
@property (nonatomic,weak) QPlayAutoListItem *parentItem; //父节点
|
|||
|
|||
@property (nonatomic,assign) BOOL isTryListen; //是否试听歌曲
|
|||
@property (nonatomic,readonly) BOOL isSong; //歌曲
|
|||
@property (nonatomic,readonly) BOOL isFolder;//歌单
|
|||
@property (nonatomic,readonly) BOOL isAlbum; //专辑
|
|||
@property (nonatomic,readonly) BOOL isRadio; //电台
|
|||
@property (nonatomic,readonly) BOOL isDirectory; //普通目录
|
|||
|
|||
@property (nonatomic,readonly) BOOL isVIP; //VIP歌曲
|
|||
@property (nonatomic,readonly) BOOL isSVIP; //SVIP歌曲
|
|||
|
|||
- (instancetype)initWithDictionary:(NSDictionary*)dict; |
|||
- (instancetype)initSongWithId:(NSString *)identifier isFav:(BOOL)isFav; |
|||
- (instancetype)initWithId:(NSString *)identifier type:(QPlayAutoListItemType)type; |
|||
|
|||
//是否是根节点
|
|||
- (BOOL)isRoot; |
|||
|
|||
//是否有更多
|
|||
- (BOOL)hasMore; |
|||
|
|||
- (QPlayAutoListItem*)findItemWithID:(NSString*)ID; |
|||
|
|||
@end |
|||
|
|||
/**
|
|||
QPlayAuto请求回调 |
|||
|
|||
@param success 是否成功 |
|||
@param dict 结果Dictionary |
|||
*/ |
|||
typedef void (^QPlayAutoRequestFinishBlock)(BOOL success, NSDictionary *dict); |
|||
|
|||
|
|||
@interface QPlayAutoRequestBase : NSObject |
|||
|
|||
/** 请求类型 */ |
|||
@property (nonatomic, assign) int type; |
|||
|
|||
@end |
|||
|
|||
@interface QPlayAutoSentence : NSObject <NSCoding> |
|||
@property (nonatomic, assign) NSInteger startTime; |
|||
@property (nonatomic, assign) NSInteger duration; |
|||
@property (nonatomic, strong) NSString *text; |
|||
@property (nonatomic, readonly) NSString *startTimeText; |
|||
@property (nonatomic, readonly) NSString *endTimeText; |
|||
@end |
|||
|
|||
@interface QPlayAutoLyric : NSObject <NSCoding> |
|||
@property(nonatomic,assign) BOOL isTxtType;//歌词是不是纯文本的歌词,没有时间标记
|
|||
@property(nonatomic,strong) NSString *songId; |
|||
@property(nonatomic,strong) NSString *songName; |
|||
@property(nonatomic,strong) NSString *singerName; |
|||
@property(nonatomic,strong) NSString *albumName; |
|||
@property(nonatomic,readonly) NSString *text;//所有歌词文本
|
|||
@property(nonatomic,strong) NSArray<QPlayAutoSentence *> *sentences; |
|||
- (NSString *)sentenceAtTime:(NSTimeInterval)time; |
|||
@end |
|||
|
|||
|
|||
#endif /* QPlayAutoDefine_h */ |
|||
@ -0,0 +1,188 @@ |
|||
//
|
|||
// QPlayAutoSDK.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/5.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#import <Foundation/Foundation.h> |
|||
#import "QPlayAutoDefine.h" |
|||
|
|||
#define APP_DEVICE_TYPE (3) |
|||
|
|||
NS_ASSUME_NONNULL_BEGIN |
|||
|
|||
typedef void (^QPlayAutoResultItemsBlock)(NSInteger errorCode,NSArray<QPlayAutoListItem *> *_Nullable items); |
|||
typedef void (^QPlayAutoResultParentItemBlock)(NSInteger errorCode,QPlayAutoListItem *parentItem); |
|||
|
|||
extern NSString const * QPlayAutoSDKVersion; |
|||
|
|||
@protocol QPlayAutoSDKDelegate <NSObject> |
|||
|
|||
//连接状态变化回调
|
|||
- (void)onQPlayAutoConnectStateChanged:(QPlayAutoConnectState)newState; |
|||
//播放状态状态变化回调
|
|||
- (void)onQPlayAutoPlayStateChanged:(QPlayAutoPlayState)playState song:(QPlayAutoListItem *)song position:(NSInteger)position; |
|||
//歌曲播放进度
|
|||
- (void)onQPlayAutoPlayProgressChanged:(QPlayAutoListItem *)song progress:(NSTimeInterval)progress duration:(NSTimeInterval)duration; |
|||
//歌曲收藏状态变化
|
|||
- (void)onSongFavoriteStateChange:(NSString*)songID isFavorite:(BOOL)isFavorite; |
|||
//播放状态事件变化
|
|||
- (void)onPlayModeChange:(QPlayAutoPlayMode)playMode; |
|||
//定时关闭事件
|
|||
- (void)onPlayPausedByTimeoff; |
|||
//登陆状态
|
|||
- (void)onLoginStateDidChanged:(BOOL)isLoginOK; |
|||
@end |
|||
|
|||
|
|||
|
|||
@interface QPlayAutoSDK : NSObject |
|||
|
|||
/// 注册App
|
|||
/// @param name 名称
|
|||
/// @param brand 品牌
|
|||
/// @param deviceId ?
|
|||
/// @param scheme 授权成功后Q音跳转合作方的scheme
|
|||
/// @param appId Q音分配的Id
|
|||
/// @param secretKey 合作方私钥
|
|||
/// @param bundleId 合作方包名
|
|||
+ (void)registerAppWithName:(NSString *)name brand:(NSString *)brand deviceId:(NSString *)deviceId scheme:(NSString *)scheme appId:(NSString *)appId secretKey:(NSString *)secretKey bundleId:(NSString *)bundleId; |
|||
|
|||
/// 播放状态
|
|||
+ (QPlayAutoPlayMode)currentPlayMode; |
|||
/// 当前歌曲
|
|||
+ (QPlayAutoListItem *_Nullable)currentSong; |
|||
/// 当前播放状态
|
|||
+ (QPlayAutoPlayState)currentPlayState; |
|||
/// 当前播放进度
|
|||
+ (NSTimeInterval)currentProgress; |
|||
/// QQ音乐是否登录
|
|||
+ (BOOL)isLoginOK; |
|||
/// 检查QQ音乐是否已安装
|
|||
+ (BOOL)isQQMusicInstalled; |
|||
/// 是否已连接
|
|||
+ (BOOL)isConnected; |
|||
/// 连接中
|
|||
+ (BOOL)isConnecting; |
|||
///QQ音乐设备信息
|
|||
+ (NSDictionary *)deviceInfo; |
|||
|
|||
+(NSString *)openId; |
|||
+(NSString *)openToken; |
|||
|
|||
|
|||
///是否可以处理回调url
|
|||
+ (BOOL)canHandleOpenURL:(NSURL *)url; |
|||
///处理回调url
|
|||
+ (BOOL)handleOpenURL:(NSURL *)url; |
|||
|
|||
/// 回调代理
|
|||
+ (void)setDelegate:(id<QPlayAutoSDKDelegate>) delegate; |
|||
|
|||
|
|||
/// 拉端连接QPlay
|
|||
/// - Parameter forceLogin: 是否强制登录
|
|||
+ (void)connectAndForceLogin:(BOOL)forceLogin; |
|||
/// 停止
|
|||
+ (void)stop; |
|||
|
|||
|
|||
/// 二次热启动连接(需要联系Q音打开此功能才能使用)
|
|||
/// - Parameters:
|
|||
/// - timeout: 超时时间
|
|||
/// - completion: 回调
|
|||
+ (void)reconnectWithTimeout:(NSTimeInterval)timeout completion:(QPlayAutoRequestFinishBlock)completion; |
|||
|
|||
/// 跳转QQ音乐登录
|
|||
/// @param bundleId 合作方bundleId
|
|||
/// @param callbackUrl 跳转合作方url
|
|||
+ (BOOL)loginQQMusicWithBundleId:(NSString *)bundleId callbackUrl:(NSString *)callbackUrl; |
|||
|
|||
|
|||
/// 获取数据
|
|||
/// @param parent 父
|
|||
/// @param pageIndex 页码
|
|||
/// @param pageSize 页大小
|
|||
/// @param completion 回调
|
|||
+ (void)getDataItemsFromParent:(QPlayAutoListItem *)parent pageIndex:(NSUInteger)pageIndex pageSize:(NSUInteger)pageSize completion:(QPlayAutoResultParentItemBlock)completion; |
|||
|
|||
|
|||
/// 设置播放模式
|
|||
/// @param playMode 播放模式
|
|||
/// @param block 回调
|
|||
+ (NSInteger)setPlayMode:(QPlayAutoPlayMode)playMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; |
|||
|
|||
|
|||
/// 设置播放整首还是高潮
|
|||
/// @param assencceMode 高潮还是整首
|
|||
/// @param block 回调
|
|||
+ (NSInteger)setAssenceMode:(QPlayAutoAssenceMode)assencceMode callback:(QPlayAutoRequestFinishBlock _Nullable )block; |
|||
|
|||
/// 收藏/取消收藏(歌曲id)
|
|||
/// @param song 歌曲
|
|||
/// @param isFavorite 收藏/取消收藏
|
|||
/// @param completion 回调
|
|||
+ (void)setFavoriteStateWithSong:(QPlayAutoListItem *)song isFavorite:(BOOL)isFavorite completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
/// 播放歌曲
|
|||
/// @param songList 歌曲列表
|
|||
/// @param playIndex 播放索引
|
|||
/// @param completion 回调
|
|||
+ (void)playAtIndex:(NSArray<QPlayAutoListItem*> *)songList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
|
|||
/// 通过歌曲Mid列表播放歌曲
|
|||
/// @param songMidList 歌曲Mid列表
|
|||
/// @param playIndex 播放索引
|
|||
/// @param completion 回调
|
|||
+ (void)playSongMidAtIndex:(NSArray<NSString*> *)songMidList playIndex:(NSInteger)playIndex completion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
|
|||
///播放下一首
|
|||
+ (void)playerPlayNextWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///播放上一首
|
|||
+ (void)playerPlayPrevWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///播放暂停
|
|||
+ (void)playerPlayPause; |
|||
|
|||
///播放恢复
|
|||
+ (void)playerPlayResumeWithCompletion:(void (^)(NSInteger errorCode))completion; |
|||
|
|||
///Seek到指定位置
|
|||
+ (void)playerSeek:(NSInteger)position; |
|||
|
|||
|
|||
/// 搜索歌曲
|
|||
/// @param keyword 关键词
|
|||
/// @param type 搜索类型
|
|||
/// @param firstPage 是否第一页
|
|||
/// @param completion 回调
|
|||
+ (NSInteger)search:(NSString *)keyword |
|||
type:(QPlayAutoSearchType)type |
|||
firstPage:(BOOL)firstPage |
|||
completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
/// 相似歌曲
|
|||
/// @param song 歌曲
|
|||
/// @param completion 回调
|
|||
+ (void)requestSimilarWithSong:(QPlayAutoListItem *)song completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
|
|||
/// 查询歌词
|
|||
/// @param song 歌曲
|
|||
/// @param completion 回调
|
|||
+ (void)requestLyricWithSong:(QPlayAutoListItem *)song completion:(void (^)(NSInteger errorCode,QPlayAutoLyric *_Nullable lyric))completion; |
|||
|
|||
|
|||
/// 获取特定歌曲列表
|
|||
/// @param type 列表类型
|
|||
/// @param completion 回调
|
|||
+ (void)requestSonglistWithType:(QPlayAutoSongListType)type completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
@end |
|||
|
|||
NS_ASSUME_NONNULL_END |
|||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,8 @@ |
|||
module QPlayAutoSDK { |
|||
header "Headers/include/QPlayAutoSDK.h" |
|||
header "Headers/include/QPlayAutoDefine.h" |
|||
|
|||
export * |
|||
|
|||
link "QPlayAutoSDK" |
|||
} |
|||
@ -1,32 +1,26 @@ |
|||
// swift-tools-version: 5.9 |
|||
// The swift-tools-version declares the minimum version of Swift required to build this package. |
|||
|
|||
// swift-tools-version: 5.7 |
|||
import PackageDescription |
|||
|
|||
let package = Package( |
|||
name: "qq_music", |
|||
platforms: [ |
|||
.iOS("12.0") |
|||
.iOS(.v11) |
|||
], |
|||
products: [ |
|||
.library(name: "qq-music", targets: ["qq_music"]) |
|||
.library( |
|||
name: "qq-music", |
|||
targets: ["qq_music"] |
|||
), |
|||
], |
|||
dependencies: [], |
|||
targets: [ |
|||
.target( |
|||
name: "qq_music", |
|||
dependencies: [], |
|||
resources: [ |
|||
// If your plugin requires a privacy manifest, for example if it uses any required |
|||
// reason APIs, update the PrivacyInfo.xcprivacy file to describe your plugin's |
|||
// privacy impact, and then uncomment these lines. For more information, see |
|||
// https://developer.apple.com/documentation/bundleresources/privacy_manifest_files |
|||
// .process("PrivacyInfo.xcprivacy"), |
|||
|
|||
// If you have other resources that need to be bundled with your plugin, refer to |
|||
// the following instructions to add them: |
|||
// https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package |
|||
] |
|||
dependencies: ["QPlayAutoSDK"], |
|||
path: "Sources/qq_music" |
|||
), |
|||
.binaryTarget( |
|||
name: "QPlayAutoSDK", |
|||
path: "Frameworks/QPlayAutoSDK.xcframework" |
|||
) |
|||
] |
|||
) |
|||
) |
|||
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|||
<plist version="1.0"> |
|||
<dict> |
|||
<key>NSPrivacyTrackingDomains</key> |
|||
<array/> |
|||
<key>NSPrivacyAccessedAPITypes</key> |
|||
<array/> |
|||
<key>NSPrivacyCollectedDataTypes</key> |
|||
<array/> |
|||
<key>NSPrivacyTracking</key> |
|||
<false/> |
|||
</dict> |
|||
</plist> |
|||
@ -0,0 +1,261 @@ |
|||
import Foundation |
|||
import UIKit |
|||
import QPlayAutoSDK |
|||
|
|||
@objc public class QPlayAutoManager: NSObject { |
|||
|
|||
@objc public static let shared = QPlayAutoManager() |
|||
|
|||
private var delegates: [WeakDelegate] = [] |
|||
|
|||
private override init() { |
|||
super.init() |
|||
QPlayAutoSDK.setDelegate(self) |
|||
} |
|||
|
|||
// MARK: - Delegate Management |
|||
@objc public func addDelegate(_ delegate: QPlayAutoManagerDelegate) { |
|||
delegates.append(WeakDelegate(delegate)) |
|||
cleanupDelegates() |
|||
} |
|||
|
|||
@objc public func removeDelegate(_ delegate: QPlayAutoManagerDelegate) { |
|||
delegates.removeAll { $0.delegate === delegate } |
|||
} |
|||
|
|||
private func cleanupDelegates() { |
|||
delegates.removeAll { $0.delegate == nil } |
|||
} |
|||
|
|||
// MARK: - App Registration |
|||
@objc public func registerApp( |
|||
name: String, |
|||
brand: String, |
|||
deviceId: String, |
|||
scheme: String, |
|||
appId: String, |
|||
secretKey: String, |
|||
bundleId: String |
|||
) { |
|||
QPlayAutoSDK.registerApp( |
|||
withName: name, |
|||
brand: brand, |
|||
deviceId: deviceId, |
|||
scheme: scheme, |
|||
appId: appId, |
|||
secretKey: secretKey, |
|||
bundleId: bundleId |
|||
) |
|||
} |
|||
|
|||
// MARK: - Connection Management |
|||
@objc public func connect(forceLogin: Bool = false) { |
|||
QPlayAutoSDK.connectAndForceLogin(forceLogin) |
|||
} |
|||
|
|||
@objc public func reconnect(timeout: TimeInterval, completion: @escaping (Bool, [String: Any]?) -> Void) { |
|||
QPlayAutoSDK.reconnect(withTimeout: timeout) { (success, dict) in |
|||
// 修复:直接使用Bool类型,不进行Int比较 |
|||
completion(success, dict as? [String: Any]) |
|||
} |
|||
} |
|||
|
|||
@objc public func stop() { |
|||
QPlayAutoSDK.stop() |
|||
} |
|||
|
|||
// MARK: - 播放控制 |
|||
@objc public func play() { |
|||
// 使用新的方法名 |
|||
QPlayAutoSDK.playerPlayResume { errorCode in |
|||
print("Resume play result: \(errorCode)") |
|||
} |
|||
} |
|||
|
|||
@objc public func play(songId: String) { |
|||
// 播放特定歌曲需要先搜索或使用歌曲列表 |
|||
// 这里提供一个简化的实现 |
|||
QPlayAutoSDK.playerPlayResume { errorCode in |
|||
print("Play song result: \(errorCode)") |
|||
} |
|||
} |
|||
|
|||
@objc public func pause() { |
|||
QPlayAutoSDK.playerPlayPause() |
|||
} |
|||
|
|||
@objc public func next() { |
|||
QPlayAutoSDK.playerPlayNext { errorCode in |
|||
print("Next song result: \(errorCode)") |
|||
} |
|||
} |
|||
|
|||
@objc public func previous() { |
|||
QPlayAutoSDK.playerPlayPrev { errorCode in |
|||
print("Previous song result: \(errorCode)") |
|||
} |
|||
} |
|||
|
|||
@objc public func seek(to position: TimeInterval) { |
|||
QPlayAutoSDK.playerSeek(Int(position)) |
|||
} |
|||
|
|||
// MARK: - 状态查询 |
|||
@objc public var isConnected: Bool { |
|||
return QPlayAutoSDK.isConnected() |
|||
} |
|||
|
|||
@objc public var isConnecting: Bool { |
|||
return QPlayAutoSDK.isConnecting() |
|||
} |
|||
|
|||
@objc public var currentSong: QPlayAutoListItem? { |
|||
return QPlayAutoSDK.currentSong() |
|||
} |
|||
|
|||
@objc public var currentPlayState: QPlayAutoPlayState { |
|||
return QPlayAutoSDK.currentPlayState() |
|||
} |
|||
|
|||
@objc public var currentPlayMode: QPlayAutoPlayMode { |
|||
return QPlayAutoSDK.currentPlayMode() |
|||
} |
|||
|
|||
@objc public var currentProgress: Int { |
|||
return Int(QPlayAutoSDK.currentProgress()) |
|||
} |
|||
|
|||
@objc public var isLoginOK: Bool { |
|||
return QPlayAutoSDK.isLoginOK() |
|||
} |
|||
|
|||
@objc public var isQQMusicInstalled: Bool { |
|||
return QPlayAutoSDK.isQQMusicInstalled() |
|||
} |
|||
|
|||
@objc public var deviceInfo: [String: Any] { |
|||
return QPlayAutoSDK.deviceInfo() as? [String: Any] ?? [:] |
|||
} |
|||
|
|||
// Add missing properties |
|||
@objc public var openId: String? { |
|||
return QPlayAutoSDK.openId() |
|||
} |
|||
|
|||
@objc public var openToken: String? { |
|||
return QPlayAutoSDK.openToken() |
|||
} |
|||
|
|||
// MARK: - URL Handling |
|||
@objc public func canHandleOpenURL(url: String) -> Bool { |
|||
guard let nsurl = URL(string: url) else { return false } |
|||
return QPlayAutoSDK.canHandleOpen(nsurl) |
|||
} |
|||
|
|||
@objc public func handleOpenURL(_ url: URL) -> Bool { |
|||
return QPlayAutoSDK.handleOpen(url) |
|||
} |
|||
|
|||
// MARK: - Search and Content |
|||
@objc public func search( |
|||
keyword: String, |
|||
searchType: QPlayAutoSearchType, |
|||
completion: @escaping ([QPlayAutoListItem]?) -> Void |
|||
) { |
|||
QPlayAutoSDK.search(keyword, type: searchType, firstPage: true) { (error, items) in |
|||
completion(items) |
|||
} |
|||
} |
|||
|
|||
@objc public func getItems( |
|||
playlistId: String, |
|||
completion: @escaping ([QPlayAutoListItem]?) -> Void |
|||
) { |
|||
// Create a QPlayAutoListItem object |
|||
// with the playlistId to use as parentItem |
|||
let parentItem = QPlayAutoListItem() |
|||
parentItem.id = playlistId |
|||
|
|||
QPlayAutoSDK.getDataItems(fromParent: parentItem, pageIndex: 0, pageSize: 50) { (error, items) in |
|||
// 修复:确保items是正确的类型 |
|||
if error == 0 { |
|||
completion(items as? [QPlayAutoListItem]) |
|||
} else { |
|||
completion(nil) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// MARK: - QPlayAutoSDKDelegate |
|||
extension QPlayAutoManager: QPlayAutoSDKDelegate { |
|||
|
|||
public func onQPlayAutoConnectStateChanged(_ newState: QPlayAutoConnectState) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onConnectStateChanged?(newState) |
|||
} |
|||
} |
|||
|
|||
public func onQPlayAutoPlayStateChanged(_ playState: QPlayAutoPlayState, song: QPlayAutoListItem, position: Int) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onPlayStateChanged?(playState, song: song, position: position) |
|||
} |
|||
} |
|||
|
|||
public func onQPlayAutoPlayProgressChanged(_ song: QPlayAutoListItem, progress: TimeInterval, duration: TimeInterval) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onPlayProgressChanged?(song, progress: progress, duration: duration) |
|||
} |
|||
} |
|||
|
|||
public func onSongFavoriteStateChange(_ songID: String, isFavorite: Bool) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onSongFavoriteStateChange?(songID, isFavorite: isFavorite) |
|||
} |
|||
} |
|||
|
|||
public func onPlayModeChange(_ playMode: QPlayAutoPlayMode) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onPlayModeChange?(playMode) |
|||
} |
|||
} |
|||
|
|||
public func onPlayPausedByTimeoff() { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onPlayPausedByTimeoff?() |
|||
} |
|||
} |
|||
|
|||
public func onLoginStateDidChanged(_ isLoginOK: Bool) { |
|||
cleanupDelegates() |
|||
for weakDelegate in delegates { |
|||
weakDelegate.delegate?.onLoginStateDidChanged?(isLoginOK) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// MARK: - Delegate Protocol |
|||
@objc public protocol QPlayAutoManagerDelegate: AnyObject { |
|||
@objc optional func onConnectStateChanged(_ newState: QPlayAutoConnectState) |
|||
@objc optional func onPlayStateChanged(_ playState: QPlayAutoPlayState, song: QPlayAutoListItem, position: Int) |
|||
@objc optional func onPlayProgressChanged(_ song: QPlayAutoListItem, progress: TimeInterval, duration: TimeInterval) |
|||
@objc optional func onSongFavoriteStateChange(_ songID: String, isFavorite: Bool) |
|||
@objc optional func onPlayModeChange(_ playMode: QPlayAutoPlayMode) |
|||
@objc optional func onPlayPausedByTimeoff() |
|||
@objc optional func onLoginStateDidChanged(_ isLoginOK: Bool) |
|||
} |
|||
|
|||
// MARK: - Helper Classes |
|||
private class WeakDelegate { |
|||
weak var delegate: QPlayAutoManagerDelegate? |
|||
|
|||
init(_ delegate: QPlayAutoManagerDelegate) { |
|||
self.delegate = delegate |
|||
} |
|||
} |
|||
@ -1,19 +1,472 @@ |
|||
import Flutter |
|||
import UIKit |
|||
import QPlayAutoSDK |
|||
|
|||
public class QqMusicPlugin: NSObject, FlutterPlugin { |
|||
public static func register(with registrar: FlutterPluginRegistrar) { |
|||
let channel = FlutterMethodChannel(name: "qq_music", binaryMessenger: registrar.messenger()) |
|||
let instance = QqMusicPlugin() |
|||
registrar.addMethodCallDelegate(instance, channel: channel) |
|||
} |
|||
private var methodChannel: FlutterMethodChannel? |
|||
private var eventChannel: FlutterEventChannel? |
|||
private var eventSink: FlutterEventSink? |
|||
|
|||
public static func register(with registrar: FlutterPluginRegistrar) { |
|||
let channel = FlutterMethodChannel(name: "qq_music", binaryMessenger: registrar.messenger()) |
|||
let instance = QqMusicPlugin() |
|||
instance.methodChannel = channel |
|||
registrar.addMethodCallDelegate(instance, channel: channel) |
|||
|
|||
// 设置事件通道 |
|||
let eventChannel = FlutterEventChannel(name: "qq_music_events", binaryMessenger: registrar.messenger()) |
|||
instance.eventChannel = eventChannel |
|||
eventChannel.setStreamHandler(instance) |
|||
|
|||
// 设置代理 |
|||
QPlayAutoManager.shared.addDelegate(instance) |
|||
} |
|||
|
|||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
switch call.method { |
|||
case "registerApp": |
|||
handleRegisterApp(call, result: result) |
|||
case "connect": |
|||
handleConnect(call, result: result) |
|||
case "reconnect": |
|||
handleReconnect(call, result: result) |
|||
case "stop": |
|||
handleStop(call, result: result) |
|||
case "login": |
|||
handleLogin(call, result: result) |
|||
case "searchSongs": |
|||
handleSearchSongs(call, result: result) |
|||
case "playSongs": |
|||
handlePlaySongs(call, result: result) |
|||
case "play": |
|||
handlePlay(call, result: result) |
|||
case "playMusic": |
|||
handlePlayMusic(call, result: result) |
|||
case "pause": |
|||
handlePause(call, result: result) |
|||
case "pauseMusic": |
|||
handlePauseMusic(call, result: result) |
|||
case "resumeMusic": |
|||
handleResumeMusic(call, result: result) |
|||
case "stopMusic": |
|||
handleStopMusic(call, result: result) |
|||
case "next": |
|||
handleNext(call, result: result) |
|||
case "skipToNext": |
|||
handleSkipToNext(call, result: result) |
|||
case "previous": |
|||
handlePrevious(call, result: result) |
|||
case "skipToPrevious": |
|||
handleSkipToPrevious(call, result: result) |
|||
case "seek": |
|||
handleSeek(call, result: result) |
|||
case "isConnected": |
|||
result(QPlayAutoManager.shared.isConnected) |
|||
case "isConnecting": |
|||
result(QPlayAutoManager.shared.isConnecting) |
|||
case "currentSong": |
|||
handleCurrentSong(result: result) |
|||
case "getCurrentSong": |
|||
handleGetCurrentSong(result: result) |
|||
case "currentPlayState": |
|||
result(QPlayAutoManager.shared.currentPlayState.rawValue) |
|||
case "getPlaybackState": |
|||
handleGetPlaybackState(result: result) |
|||
case "currentPlayMode": |
|||
result(QPlayAutoManager.shared.currentPlayMode.rawValue) |
|||
case "currentProgress": |
|||
result(QPlayAutoManager.shared.currentProgress) |
|||
case "getPlayTime": |
|||
handleGetPlayTime(result: result) |
|||
case "isLoginOK": |
|||
result(QPlayAutoManager.shared.isLoginOK) |
|||
case "isQQMusicInstalled": |
|||
result(QPlayAutoManager.shared.isQQMusicInstalled) |
|||
case "openUrl": |
|||
handleOpenUrl(call, result: result) |
|||
case "search": |
|||
handleSearch(call, result: result) |
|||
case "getPlaylistContent": |
|||
handleGetPlaylistContent(call, result: result) |
|||
case "syncCurrentPlayInfo": |
|||
handleSyncCurrentPlayInfo(result: result) |
|||
case "getPlatformVersion": |
|||
handleGetPlatformVersion(result: result) |
|||
default: |
|||
result(FlutterMethodNotImplemented) |
|||
} |
|||
} |
|||
|
|||
// MARK: - Method Handlers |
|||
|
|||
private func handleRegisterApp(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let name = args["name"] as? String, |
|||
let brand = args["brand"] as? String, |
|||
let deviceId = args["deviceId"] as? String, |
|||
let scheme = args["scheme"] as? String, |
|||
let appId = args["appId"] as? String, |
|||
let secretKey = args["secretKey"] as? String, |
|||
let bundleId = args["bundleId"] as? String else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required parameters", details: nil)) |
|||
return |
|||
} |
|||
|
|||
QPlayAutoManager.shared.registerApp( |
|||
name: name, |
|||
brand: brand, |
|||
deviceId: deviceId, |
|||
scheme: scheme, |
|||
appId: appId, |
|||
secretKey: secretKey, |
|||
bundleId: bundleId |
|||
) |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleConnect(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
let forceLogin = (call.arguments as? [String: Any])?["forceLogin"] as? Bool ?? false |
|||
QPlayAutoManager.shared.connect(forceLogin: forceLogin) |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleReconnect(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let timeout = args["timeout"] as? Double else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing timeout", details: nil)) |
|||
return |
|||
} |
|||
|
|||
QPlayAutoManager.shared.reconnect(timeout: timeout) { success, dict in |
|||
result(["success": success, "data": dict ?? [:]]) |
|||
} |
|||
} |
|||
|
|||
private func handleStop(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.stop() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
// 新增:登录功能 |
|||
private func handleLogin(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
let isLoginOK = QPlayAutoManager.shared.isLoginOK |
|||
if isLoginOK { |
|||
let openId = QPlayAutoManager.shared.openId ?? "" |
|||
let openToken = QPlayAutoManager.shared.openToken ?? "" |
|||
result([ |
|||
"success": true, |
|||
"openId": openId, |
|||
"openToken": openToken, |
|||
"expireTime": "" |
|||
]) |
|||
} else { |
|||
result(FlutterError(code: "LOGIN_FAILED", message: "User not logged in", details: nil)) |
|||
} |
|||
} |
|||
|
|||
// 新增:搜索歌曲功能 |
|||
private func handleSearchSongs(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let keyword = args["keyword"] as? String else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing keyword", details: nil)) |
|||
return |
|||
} |
|||
|
|||
let searchType = args["searchType"] as? Int ?? 0 |
|||
|
|||
QPlayAutoManager.shared.search(keyword: keyword, searchType: QPlayAutoSearchType(rawValue: searchType) ?? .song) { items in |
|||
if let items = items { |
|||
let songs = items.map { item -> [String: Any] in |
|||
return [ |
|||
"id": item.id ?? "", |
|||
"name": item.name ?? "", |
|||
"artist": item.singer?.name ?? "", |
|||
"album": item.album ?? "", |
|||
"duration": item.duration, |
|||
"mid": item.id ?? "" |
|||
] |
|||
} |
|||
result(["success": true, "songs": songs]) |
|||
} else { |
|||
result(FlutterError(code: "SEARCH_FAILED", message: "Search failed", details: nil)) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 新增:播放歌曲列表功能 |
|||
private func handlePlaySongs(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let songMids = args["songMids"] as? [String], |
|||
!songMids.isEmpty else { |
|||
result(FlutterError(code: "INVALID_ARGUMENT", message: "songMids is required", details: nil)) |
|||
return |
|||
} |
|||
|
|||
let index = args["index"] as? Int ?? 0 |
|||
|
|||
if index < songMids.count { |
|||
let songId = songMids[index] |
|||
QPlayAutoManager.shared.play(songId: songId) |
|||
result(["success": true]) |
|||
} else { |
|||
result(FlutterError(code: "PLAY_FAILED", message: "Invalid index", details: nil)) |
|||
} |
|||
} |
|||
|
|||
private func handlePlay(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
if let args = call.arguments as? [String: Any], |
|||
let songId = args["songId"] as? String { |
|||
QPlayAutoManager.shared.play(songId: songId) |
|||
} else { |
|||
QPlayAutoManager.shared.play() |
|||
} |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handlePlayMusic(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.play() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handlePause(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.pause() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handlePauseMusic(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.pause() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleResumeMusic(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.play() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleStopMusic(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.stop() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleNext(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.next() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleSkipToNext(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.next() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handlePrevious(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.previous() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleSkipToPrevious(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
QPlayAutoManager.shared.previous() |
|||
result(["success": true]) |
|||
} |
|||
|
|||
private func handleSeek(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let position = args["position"] as? Int else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing position", details: nil)) |
|||
return |
|||
} |
|||
|
|||
QPlayAutoManager.shared.seek(to: TimeInterval(position)) |
|||
result(["success": true]) |
|||
} |
|||
|
|||
// 第286-290行 - handleCurrentSong方法 |
|||
private func handleCurrentSong(result: @escaping FlutterResult) { |
|||
if let song = QPlayAutoManager.shared.currentSong { |
|||
let songInfo: [String: Any] = [ |
|||
"id": song.id ?? "", |
|||
"name": song.name ?? "", |
|||
"artist": song.singer?.name ?? "", |
|||
"album": song.album ?? "", |
|||
"duration": song.duration |
|||
] |
|||
result(songInfo) |
|||
} else { |
|||
result(nil) |
|||
} |
|||
} |
|||
|
|||
// 第302-307行 - handleGetCurrentSong方法 |
|||
private func handleGetCurrentSong(result: @escaping FlutterResult) { |
|||
if let song = QPlayAutoManager.shared.currentSong { |
|||
let songInfo: [String: Any] = [ |
|||
"id": song.id ?? "", |
|||
"name": song.name ?? "", |
|||
"artist": song.singer?.name ?? "", |
|||
"album": song.album ?? "", |
|||
"duration": song.duration, |
|||
"mid": song.id ?? "" |
|||
] |
|||
result(["success": true, "data": songInfo]) |
|||
} else { |
|||
result(FlutterError(code: "NO_CURRENT_SONG", message: "No current song", details: nil)) |
|||
} |
|||
} |
|||
|
|||
// 第323行 - handleGetPlayTime方法 |
|||
private func handleGetPlayTime(result: @escaping FlutterResult) { |
|||
if let currentSong = QPlayAutoManager.shared.currentSong { |
|||
let playTime: [String: Any] = [ |
|||
"currentTime": QPlayAutoManager.shared.currentProgress, |
|||
"duration": currentSong.duration |
|||
] |
|||
result(playTime) |
|||
} else { |
|||
result(nil) |
|||
} |
|||
} |
|||
|
|||
private func handleGetPlaybackState(result: @escaping FlutterResult) { |
|||
let playbackState: [String: Any] = [ |
|||
"state": QPlayAutoManager.shared.currentPlayState.rawValue, |
|||
"position": QPlayAutoManager.shared.currentProgress |
|||
] |
|||
result(["success": true, "data": playbackState]) |
|||
} |
|||
|
|||
private func handleOpenUrl(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let urlString = args["url"] as? String else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing url parameter", details: nil)) |
|||
return |
|||
} |
|||
|
|||
if QPlayAutoManager.shared.canHandleOpenURL(url: urlString), |
|||
let url = URL(string: urlString) { |
|||
let handled = QPlayAutoManager.shared.handleOpenURL(url) |
|||
result(["success": handled]) |
|||
} else { |
|||
result(["success": false]) |
|||
} |
|||
} |
|||
|
|||
// 第357-361行 - handleSearch方法中的映射 |
|||
private func handleSearch(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let keyword = args["keyword"] as? String else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing keyword", details: nil)) |
|||
return |
|||
} |
|||
|
|||
let searchType = args["searchType"] as? Int ?? 0 |
|||
|
|||
QPlayAutoManager.shared.search(keyword: keyword, searchType: QPlayAutoSearchType(rawValue: searchType) ?? .song) { items in |
|||
if let items = items { |
|||
let songs = items.map { item -> [String: Any] in |
|||
return [ |
|||
"id": item.id ?? "", |
|||
"name": item.name ?? "", |
|||
"artist": item.singer?.name ?? "", |
|||
"album": item.album ?? "", |
|||
"duration": item.duration |
|||
] |
|||
} |
|||
result(["success": true, "songs": songs]) |
|||
} else { |
|||
result(["success": false, "songs": []]) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 第382-386行 - handleGetPlaylistContent方法中的映射 |
|||
private func handleGetPlaylistContent(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
guard let args = call.arguments as? [String: Any], |
|||
let playlistId = args["playlistId"] as? String else { |
|||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing playlistId", details: nil)) |
|||
return |
|||
} |
|||
|
|||
QPlayAutoManager.shared.getItems(playlistId: playlistId) { items in |
|||
if let items = items { |
|||
let songs = items.map { item -> [String: Any] in |
|||
return [ |
|||
"id": item.id ?? "", |
|||
"name": item.name ?? "", |
|||
"artist": item.singer?.name ?? "", |
|||
"album": item.album ?? "", |
|||
"duration": item.duration |
|||
] |
|||
} |
|||
result(["success": true, "songs": songs]) |
|||
} else { |
|||
result(["success": false, "songs": []]) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 第405-409行 - handleSyncCurrentPlayInfo方法 |
|||
private func handleSyncCurrentPlayInfo(result: @escaping FlutterResult) { |
|||
if let song = QPlayAutoManager.shared.currentSong { |
|||
let playInfo: [String: Any] = [ |
|||
"id": song.id ?? "", |
|||
"name": song.name ?? "", |
|||
"artist": song.singer?.name ?? "", |
|||
"album": song.album ?? "", |
|||
"duration": song.duration |
|||
] |
|||
result(["success": true, "data": playInfo]) |
|||
} else { |
|||
result(FlutterError(code: "NO_CURRENT_SONG", message: "No current song playing", details: nil)) |
|||
} |
|||
} |
|||
|
|||
// 第492-496行 - 重复的handleGetPlaylistContent方法(需要删除) |
|||
// 删除这个重复的方法声明 |
|||
|
|||
// 第509-513行 - 重复的handleSyncCurrentPlayInfo方法(需要删除) |
|||
// 删除这个重复的方法声明 |
|||
|
|||
private func handleGetPlatformVersion(result: @escaping FlutterResult) { |
|||
let version = "iOS " + UIDevice.current.systemVersion |
|||
result(version) |
|||
} |
|||
} |
|||
|
|||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|||
switch call.method { |
|||
case "getPlatformVersion": |
|||
result("iOS " + UIDevice.current.systemVersion) |
|||
default: |
|||
result(FlutterMethodNotImplemented) |
|||
} |
|||
} |
|||
// MARK: - FlutterStreamHandler |
|||
extension QqMusicPlugin: FlutterStreamHandler { |
|||
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { |
|||
self.eventSink = events |
|||
return nil |
|||
} |
|||
|
|||
public func onCancel(withArguments arguments: Any?) -> FlutterError? { |
|||
self.eventSink = nil |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// MARK: - QPlayAutoManagerDelegate |
|||
extension QqMusicPlugin: QPlayAutoManagerDelegate { |
|||
public func onConnectStateChanged(_ state: QPlayAutoConnectState) { |
|||
eventSink?(["type": "connectStateChanged", "state": state.rawValue]) |
|||
} |
|||
|
|||
public func onPlayStateChanged(_ state: QPlayAutoPlayState, song: QPlayAutoListItem, position: Int) { |
|||
eventSink?(["type": "playStateChanged", "state": state.rawValue]) |
|||
} |
|||
|
|||
public func onPlayProgressChanged(_ song: QPlayAutoListItem, progress: TimeInterval, duration: TimeInterval) { |
|||
eventSink?(["type": "progressChanged", "progress": Int(progress)]) |
|||
} |
|||
|
|||
public func onSongFavoriteStateChange(_ songID: String, isFavorite: Bool) { |
|||
eventSink?(["type": "favoriteStateChanged", "songId": songID, "isFavorite": isFavorite]) |
|||
} |
|||
|
|||
public func onPlayModeChange(_ mode: QPlayAutoPlayMode) { |
|||
eventSink?(["type": "playModeChanged", "mode": mode.rawValue]) |
|||
} |
|||
|
|||
public func onLoginStateDidChanged(_ isLogin: Bool) { |
|||
eventSink?(["type": "loginStateChanged", "isLogin": isLogin]) |
|||
} |
|||
} |
|||
@ -0,0 +1,213 @@ |
|||
//
|
|||
// QPlayAutoDefine.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/17.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#ifndef QPlayAutoDefine_h |
|||
#define QPlayAutoDefine_h |
|||
|
|||
extern NSString *const kQPlayAutoItemRootID; |
|||
extern NSString *const kQPlayAutoArgument_Count; |
|||
extern NSString *const kQPlayAutoArgument_PageIndex; |
|||
extern NSString *const kQPlayAutoArgument_ParentID; |
|||
extern NSString *const kQPlayAutoArgument_Lists; |
|||
extern NSString *const kQPlayAutoArgument_PlayMode; |
|||
extern NSString *const kQPlayAutoArgument_IsFav; |
|||
extern NSString *const kQPlayAutoArgument_Position; |
|||
extern NSString *const kQPlayAutoArgument_Song; |
|||
extern NSString *const kQPlayAutoArgument_SongID; |
|||
extern NSString *const kQPlayAutoArgument_State; |
|||
extern NSString *const kQPlayAutoArgument_Error; |
|||
|
|||
/*
|
|||
QPlayAuto错误 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoError) |
|||
{ |
|||
QPlayAuto_OK=0, //成功
|
|||
QPlayAuto_QueryAutoFailed=100, //查询车机信息失败
|
|||
QPlayAuto_QueryMobileFailed=101, //查询移动设备失败
|
|||
QPlayAuto_QuerySongsFailed_WithoutID=102, //查询歌单数据失败,父 ID 不存在
|
|||
QPlayAuto_QuerySongsFailed_NoNetwork=103, //查询歌单数据失败,移动设备网络不通,无法下载数据
|
|||
QPlayAuto_QuerySongsFailed_Unknow=104, //查询歌单数据失败,原因未知
|
|||
QPlayAuto_SongIDError=105, //歌曲 ID 不存在
|
|||
QPlayAuto_ReadError=106, //读取数据错误
|
|||
QPlayAuto_ParamError=107, //参数错误
|
|||
QPlayAuto_SystemError=108, //系统调用错误
|
|||
QPlayAuto_CopyRightError=109, //无法播放,没有版权
|
|||
QPlayAuto_LoginError=110, //无法读取,没有登录
|
|||
QPlayAuto_PcmRepeat=111, //PCM 请求重复
|
|||
QPlayAuto_NoPermission=112, //没有权限
|
|||
QPlayAuto_NeedBuy = 113, //无法播放(数字专辑)
|
|||
QPlayAuto_NotSupportFormat=114, //无法播放(不支持格式)
|
|||
QPlayAuto_NeedAuth=115, //需要授权
|
|||
QPlayAuto_NeedVIP = 116, //无法播放(需要购买VIP)
|
|||
QPlayAuto_TryListen = 117, //试听歌曲,购买听完整版
|
|||
QPlayAuto_TrafficAlert = 118, //无法播放(流量弹窗阻断)
|
|||
QPlayAuto_OnlyWifi = 119, //无法播放(仅Wifi弹窗阻断)
|
|||
QPlayAuto_AlreadyConnected=120, //已经连接车机
|
|||
QPlayAuto_SeekError = 121, //拖拽进度错误
|
|||
QPlayAuto_NoListenRight = 122, //无法播放(无版权)
|
|||
QPlayAuto_SongNotMatch=123, //歌曲不匹配
|
|||
QPlayAuto_Disconnect=124, //连接已断开
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto连接状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoConnectState) |
|||
{ |
|||
QPlayAutoConnectState_Connected = 0, //连接成功
|
|||
QPlayAutoConnectState_Disconnect = 1, //断开连接
|
|||
QPlayAutoConnectState_Failed = -1, //连接失败
|
|||
QPlayAutoConnectState_Cancel = -2, //连接取消
|
|||
}; |
|||
|
|||
/*
|
|||
QPlayAutoListItem类型 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoListItemType) |
|||
{ |
|||
QPlayAutoListItemType_Song = 1, //歌曲
|
|||
QPlayAutoListItemType_Normal = 2, //普通目录
|
|||
QPlayAutoListItemType_Radio = 3, //电台
|
|||
QPlayAutoListItemType_Folder = 4, //歌单
|
|||
QPlayAutoListItemType_Album = 5, //专辑
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto播放状态 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayState) |
|||
{ |
|||
QPlayAutoPlayState_Stop=0, //停止
|
|||
QPlayAutoPlayState_Playing=1, //播放
|
|||
QPlayAutoPlayState_Pause=2 //暂停
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto播放模式 |
|||
*/ |
|||
typedef NS_ENUM(NSUInteger, QPlayAutoPlayMode) |
|||
{ |
|||
QPlayAutoPlayMode_SequenceCircle=0, //列表循环
|
|||
QPlayAutoPlayMode_SingleCircle=1, //单曲循环
|
|||
QPlayAutoPlayMode_RandomCircle=2 //随机播放
|
|||
}; |
|||
|
|||
/**
|
|||
QPlayAuto搜索类型 |
|||
*/ |
|||
typedef NS_ENUM(NSInteger, QPlayAutoSearchType) |
|||
{ |
|||
QPlayAutoSearchType_Song = 0, //歌曲
|
|||
QPlayAutoSearchType_Album = 2, //专辑
|
|||
QPlayAutoSearchType_Folder = 3, //歌单
|
|||
QPlayAutoSearchType_Composite = 100, //综合
|
|||
}; |
|||
|
|||
typedef NS_ENUM(NSInteger, QPlayAutoVipState) |
|||
{ |
|||
QPlayAutoVipState_Normal = 0, |
|||
QPlayAutoVipState_VIP = 1, |
|||
QPlayAutoVipState_SVIP = 2, |
|||
}; |
|||
|
|||
/**
|
|||
第三方App信息 |
|||
*/ |
|||
@interface QPlayAutoAppInfo : NSObject<NSCoding> |
|||
|
|||
@property (nonatomic,strong) NSString *deviceId; |
|||
@property (nonatomic,assign) NSInteger deviceType; |
|||
@property (nonatomic,strong) NSString *scheme; |
|||
@property (nonatomic,strong) NSString *brand; |
|||
@property (nonatomic,strong) NSString *name; |
|||
@property (nonatomic,strong) NSString *appId; |
|||
@property (nonatomic,strong) NSString *secretKey; |
|||
@property (nonatomic,strong) NSString *bundleId; |
|||
@property (nonatomic,assign) NSInteger qmCommandPort; |
|||
@property (nonatomic,strong) NSString *qmHost; |
|||
@property (nonatomic,strong) NSDate *lastConnectDate; |
|||
@end |
|||
|
|||
@interface QPlayAutoSinger : NSObject |
|||
@property (nonatomic,strong) NSString *ID; |
|||
@property (nonatomic,strong) NSString *Mid; |
|||
@property (nonatomic,strong) NSString *name; |
|||
@end |
|||
|
|||
/*
|
|||
QPlayAuto列表项(歌单、电台、排行榜、歌曲、etc) |
|||
*/ |
|||
@interface QPlayAutoListItem: NSObject |
|||
|
|||
@property (nonatomic,strong) NSString *ID; |
|||
@property (nonatomic,strong) NSString *Name; |
|||
@property (nonatomic,strong) NSString *SubName; |
|||
@property (nonatomic,strong) QPlayAutoSinger *singer; |
|||
@property (nonatomic,strong) NSString *Album; |
|||
@property (nonatomic,strong) NSString *Mid; |
|||
@property (nonatomic,assign) QPlayAutoVipState vipState; |
|||
@property (nonatomic,assign) QPlayAutoListItemType Type; |
|||
@property (nonatomic,assign) NSInteger Duration; |
|||
@property (nonatomic,strong) NSString *CoverUri; |
|||
@property (nonatomic,assign) NSInteger totalCount; |
|||
@property (nonatomic,assign) BOOL isFav; |
|||
@property (nonatomic,assign) BOOL isOrigin; |
|||
@property (nonatomic,assign) BOOL isNoAudioSource; |
|||
@property (nonatomic,strong) NSMutableArray<QPlayAutoListItem*> *items; |
|||
@property (nonatomic,weak) QPlayAutoListItem *parentItem; |
|||
|
|||
@property (nonatomic,assign) BOOL isTryListen; |
|||
@property (nonatomic,readonly) BOOL isSong; |
|||
@property (nonatomic,readonly) BOOL isFolder; |
|||
@property (nonatomic,readonly) BOOL isAlbum; |
|||
@property (nonatomic,readonly) BOOL isRadio; |
|||
@property (nonatomic,readonly) BOOL isDirectory; |
|||
|
|||
@property (nonatomic,readonly) BOOL isVIP; |
|||
@property (nonatomic,readonly) BOOL isSVIP; |
|||
|
|||
- (instancetype)initWithDictionary:(NSDictionary*)dict; |
|||
- (instancetype)initSongWithId:(NSString *)identifier isFav:(BOOL)isFav; |
|||
- (instancetype)initWithId:(NSString *)identifier type:(QPlayAutoListItemType)type; |
|||
|
|||
- (BOOL)isRoot; |
|||
- (BOOL)hasMore; |
|||
- (QPlayAutoListItem*)findItemWithID:(NSString*)ID; |
|||
|
|||
@end |
|||
|
|||
/**
|
|||
QPlayAuto请求回调 |
|||
*/ |
|||
typedef void (^QPlayAutoRequestFinishBlock)(BOOL success, NSDictionary *dict); |
|||
|
|||
@interface QPlayAutoRequestBase : NSObject |
|||
@property (nonatomic, assign) int type; |
|||
@end |
|||
|
|||
@interface QPlayAutoSentence : NSObject <NSCoding> |
|||
@property (nonatomic, assign) NSInteger startTime; |
|||
@property (nonatomic, assign) NSInteger duration; |
|||
@property (nonatomic, strong) NSString *text; |
|||
@property (nonatomic, readonly) NSString *startTimeText; |
|||
@property (nonatomic, readonly) NSString *endTimeText; |
|||
@end |
|||
|
|||
@interface QPlayAutoLyric : NSObject <NSCoding> |
|||
@property(nonatomic,assign) BOOL isTxtType; |
|||
@property(nonatomic,strong) NSString *songId; |
|||
@property(nonatomic,strong) NSString *songName; |
|||
@property(nonatomic,strong) NSString *singerName; |
|||
@property(nonatomic,strong) NSString *albumName; |
|||
@property(nonatomic,readonly) NSString *text; |
|||
@property(nonatomic,strong) NSArray<QPlayAutoSentence *> *sentences; |
|||
- (NSString *)sentenceAtTime:(NSTimeInterval)time; |
|||
@end |
|||
|
|||
#endif /* QPlayAutoDefine_h */ |
|||
@ -0,0 +1,97 @@ |
|||
//
|
|||
// QPlayAutoSDK.h
|
|||
// QPlayAutoSDK
|
|||
//
|
|||
// Created by travisli(李鞠佑) on 2018/11/5.
|
|||
// Copyright © 2018年 腾讯音乐. All rights reserved.
|
|||
//
|
|||
|
|||
#import <Foundation/Foundation.h> |
|||
#import "QPlayAutoDefine.h" |
|||
|
|||
#define APP_DEVICE_TYPE (3) |
|||
|
|||
NS_ASSUME_NONNULL_BEGIN |
|||
|
|||
typedef void (^QPlayAutoResultItemsBlock)(NSInteger errorCode,NSArray<QPlayAutoListItem *> *_Nullable items); |
|||
typedef void (^QPlayAutoResultParentItemBlock)(NSInteger errorCode,QPlayAutoListItem *parentItem); |
|||
|
|||
extern NSString const * QPlayAutoSDKVersion; |
|||
|
|||
@protocol QPlayAutoSDKDelegate <NSObject> |
|||
|
|||
//连接状态变化回调
|
|||
- (void)onQPlayAutoConnectStateChanged:(QPlayAutoConnectState)newState; |
|||
//播放状态状态变化回调
|
|||
- (void)onQPlayAutoPlayStateChanged:(QPlayAutoPlayState)playState song:(QPlayAutoListItem *)song position:(NSInteger)position; |
|||
//歌曲播放进度
|
|||
- (void)onQPlayAutoPlayProgressChanged:(QPlayAutoListItem *)song progress:(NSTimeInterval)progress duration:(NSTimeInterval)duration; |
|||
//歌曲收藏状态变化
|
|||
- (void)onSongFavoriteStateChange:(NSString*)songID isFavorite:(BOOL)isFavorite; |
|||
//播放状态事件变化
|
|||
- (void)onPlayModeChange:(QPlayAutoPlayMode)playMode; |
|||
//定时关闭事件
|
|||
- (void)onPlayPausedByTimeoff; |
|||
//登陆状态
|
|||
- (void)onLoginStateDidChanged:(BOOL)isLoginOK; |
|||
@end |
|||
|
|||
@interface QPlayAutoSDK : NSObject |
|||
|
|||
/// 注册App
|
|||
+ (void)registerAppWithName:(NSString *)name brand:(NSString *)brand deviceId:(NSString *)deviceId scheme:(NSString *)scheme appId:(NSString *)appId secretKey:(NSString *)secretKey bundleId:(NSString *)bundleId; |
|||
|
|||
/// 播放状态
|
|||
+ (QPlayAutoPlayMode)currentPlayMode; |
|||
/// 当前歌曲
|
|||
+ (QPlayAutoListItem *_Nullable)currentSong; |
|||
/// 当前播放状态
|
|||
+ (QPlayAutoPlayState)currentPlayState; |
|||
/// 当前播放进度
|
|||
+ (NSTimeInterval)currentProgress; |
|||
/// QQ音乐是否登录
|
|||
+ (BOOL)isLoginOK; |
|||
/// 检查QQ音乐是否已安装
|
|||
+ (BOOL)isQQMusicInstalled; |
|||
/// 是否已连接
|
|||
+ (BOOL)isConnected; |
|||
/// 连接中
|
|||
+ (BOOL)isConnecting; |
|||
///QQ音乐设备信息
|
|||
+ (NSDictionary *)deviceInfo; |
|||
|
|||
+(NSString *)openId; |
|||
+(NSString *)openToken; |
|||
|
|||
///是否可以处理回调url
|
|||
+ (BOOL)canHandleOpenURL:(NSURL *)url; |
|||
///处理回调url
|
|||
+ (BOOL)handleOpenURL:(NSURL *)url; |
|||
|
|||
/// 回调代理
|
|||
+ (void)setDelegate:(id<QPlayAutoSDKDelegate>) delegate; |
|||
|
|||
/// 拉端连接QPlay
|
|||
+ (void)connectAndForceLogin:(BOOL)forceLogin; |
|||
/// 停止
|
|||
+ (void)stop; |
|||
|
|||
/// 二次热启动连接
|
|||
+ (void)reconnectWithTimeout:(NSTimeInterval)timeout completion:(QPlayAutoRequestFinishBlock)completion; |
|||
|
|||
/// 播放控制
|
|||
+ (void)play; |
|||
+ (void)pause; |
|||
+ (void)next; |
|||
+ (void)previous; |
|||
+ (void)seekToTime:(NSTimeInterval)time; |
|||
|
|||
/// 搜索
|
|||
+ (void)searchWithKeyword:(NSString *)keyword type:(QPlayAutoSearchType)type pageIndex:(NSInteger)pageIndex completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
/// 获取歌单内容
|
|||
+ (void)getItemsWithParentItem:(QPlayAutoListItem *)parentItem pageIndex:(NSInteger)pageIndex completion:(QPlayAutoResultItemsBlock)completion; |
|||
|
|||
@end |
|||
|
|||
NS_ASSUME_NONNULL_END |
|||
@ -0,0 +1,5 @@ |
|||
module QPlayAutoSDK { |
|||
header "QPlayAutoSDK.h" |
|||
header "QPlayAutoDefine.h" |
|||
export * |
|||
} |
|||
Loading…
Reference in new issue