├── .github └── workflows │ └── release_ci.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── guozhigq │ │ │ │ └── pilipala │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-hdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-mdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-mdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-xxhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable-xxxhdpi-v26 │ │ │ └── ic_launcher_monochrome.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_notification_icon.png │ │ │ ├── drawable │ │ │ ├── ic_baseline_forward_10_24.xml │ │ │ ├── ic_baseline_replay_10_24.xml │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── fonts │ ├── Jura-Bold.ttf │ └── fansCard.ttf ├── images │ ├── ai.png │ ├── big-vip.png │ ├── coin.png │ ├── dm.svg │ ├── dm_gray.png │ ├── dm_white.png │ ├── error.svg │ ├── live.gif │ ├── live.png │ ├── live │ │ └── default_bg.webp │ ├── loading.gif │ ├── loading.png │ ├── logo │ │ ├── logo_android.png │ │ ├── logo_android_2.png │ │ └── logo_ios.png │ ├── lv │ │ ├── lv0.png │ │ ├── lv1.png │ │ ├── lv2.png │ │ ├── lv3.png │ │ ├── lv4.png │ │ ├── lv5.png │ │ └── lv6.png │ ├── noface.jpeg │ ├── play.png │ ├── play.svg │ ├── run-pokemon.gif │ ├── tv.svg │ ├── up.svg │ ├── up_gray.png │ ├── video.png │ ├── video │ │ ├── danmu_close.svg │ │ └── danmu_open.svg │ ├── view.svg │ ├── view_gray.png │ └── view_white.png ├── loading.json ├── screenshots │ ├── 174shots_so.png │ ├── 510shots_so.png │ ├── 850shots_so.png │ ├── bangumi.png │ ├── bangumi_detail.png │ ├── dynamic.png │ ├── home.png │ ├── main_screen.png │ ├── media.png │ ├── member.png │ ├── search.png │ └── set.png └── trail_loading.json ├── change_log ├── 1.0.0.0817.md ├── 1.0.1.0817.md ├── 1.0.10.1016.md ├── 1.0.11.1112.md ├── 1.0.12.1114.md ├── 1.0.13.1217.md ├── 1.0.14.1225.md ├── 1.0.15.0101.md ├── 1.0.16.0102.md ├── 1.0.17.0125.md ├── 1.0.18.0130.md ├── 1.0.19.0131.md ├── 1.0.2.0819.md ├── 1.0.20.0303.md ├── 1.0.21.0306.md ├── 1.0.22.0430.md ├── 1.0.23.0504.md ├── 1.0.23.0505.md ├── 1.0.24.0626.md ├── 1.0.25.1010.md ├── 1.0.3.0821.md ├── 1.0.4.0822.md ├── 1.0.5.0826.md ├── 1.0.6.0902.md ├── 1.0.7.0908.md ├── 1.0.8.0917.md └── 1.0.9.1015.md ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ └── 3.png │ ├── short_description.txt │ └── title.txt │ └── zh-CN │ ├── changelogs │ └── 2001.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ └── 3.png │ ├── short_description.txt │ └── title.txt ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-50x50@1x.png │ │ ├── Icon-App-50x50@2x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── chat_page.dart ├── common │ ├── constants.dart │ ├── pages_bottom_sheet.dart │ ├── skeleton │ │ ├── dynamic_card.dart │ │ ├── media_bangumi.dart │ │ ├── skeleton.dart │ │ ├── video_card_h.dart │ │ ├── video_card_v.dart │ │ └── video_reply.dart │ └── widgets │ │ ├── animated_dialog.dart │ │ ├── app_expansion_panel_list.dart │ │ ├── appbar.dart │ │ ├── badge.dart │ │ ├── content_container.dart │ │ ├── custom_toast.dart │ │ ├── html_render.dart │ │ ├── http_error.dart │ │ ├── live_card.dart │ │ ├── network_img_layer.dart │ │ ├── no_data.dart │ │ ├── sliver_header.dart │ │ ├── stat │ │ ├── danmu.dart │ │ └── view.dart │ │ ├── video_card_h.dart │ │ └── video_card_v.dart ├── http │ ├── api.dart │ ├── bangumi.dart │ ├── black.dart │ ├── common.dart │ ├── constants.dart │ ├── danmaku.dart │ ├── dynamics.dart │ ├── fan.dart │ ├── fav.dart │ ├── follow.dart │ ├── html.dart │ ├── index.dart │ ├── init.dart │ ├── interceptor.dart │ ├── live.dart │ ├── login.dart │ ├── member.dart │ ├── msg.dart │ ├── read.dart │ ├── reply.dart │ ├── search.dart │ ├── user.dart │ └── video.dart ├── main.dart ├── models │ ├── bangumi │ │ ├── info.dart │ │ └── list.dart │ ├── common │ │ ├── action_type.dart │ │ ├── business_type.dart │ │ ├── color_type.dart │ │ ├── dynamic_badge_mode.dart │ │ ├── dynamics_type.dart │ │ ├── gesture_mode.dart │ │ ├── index.dart │ │ ├── nav_bar_config.dart │ │ ├── rank_type.dart │ │ ├── rcmd_type.dart │ │ ├── reply_sort_type.dart │ │ ├── reply_type.dart │ │ ├── search_type.dart │ │ ├── tab_type.dart │ │ ├── theme_type.dart │ │ └── video_episode_type.dart │ ├── danmaku │ │ ├── dm.pb.dart │ │ ├── dm.pbenum.dart │ │ ├── dm.pbjson.dart │ │ ├── dm.pbserver.dart │ │ └── dm.proto │ ├── dynamics │ │ ├── result.dart │ │ └── up.dart │ ├── fans │ │ └── result.dart │ ├── follow │ │ └── result.dart │ ├── github │ │ └── latest.dart │ ├── home │ │ └── rcmd │ │ │ └── result.dart │ ├── live │ │ ├── follow.dart │ │ ├── item.dart │ │ ├── message.dart │ │ ├── quality.dart │ │ ├── room_info.dart │ │ └── room_info_h5.dart │ ├── login │ │ └── index.dart │ ├── member │ │ ├── archive.dart │ │ ├── article.dart │ │ ├── coin.dart │ │ ├── info.dart │ │ ├── like.dart │ │ ├── seasons.dart │ │ └── tags.dart │ ├── model_hot_video_item.dart │ ├── model_owner.dart │ ├── model_rec_video_item.dart │ ├── msg │ │ ├── account.dart │ │ ├── like.dart │ │ ├── reply.dart │ │ ├── session.dart │ │ └── system.dart │ ├── read │ │ ├── opus.dart │ │ └── read.dart │ ├── search │ │ ├── all.dart │ │ ├── hot.dart │ │ ├── result.dart │ │ └── suggest.dart │ ├── user │ │ ├── black.dart │ │ ├── fav_detail.dart │ │ ├── fav_folder.dart │ │ ├── history.dart │ │ ├── info.dart │ │ ├── info.g.dart │ │ ├── stat.dart │ │ ├── sub_detail.dart │ │ └── sub_folder.dart │ ├── video │ │ ├── ai.dart │ │ ├── later.dart │ │ ├── play │ │ │ ├── ao_output.dart │ │ │ ├── quality.dart │ │ │ └── url.dart │ │ ├── reply │ │ │ ├── config.dart │ │ │ ├── content.dart │ │ │ ├── data.dart │ │ │ ├── emote.dart │ │ │ ├── item.dart │ │ │ ├── member.dart │ │ │ ├── page.dart │ │ │ ├── top_replies.dart │ │ │ └── upper.dart │ │ └── subTitile │ │ │ ├── content.dart │ │ │ └── result.dart │ └── video_detail_res.dart ├── pages │ ├── about │ │ └── index.dart │ ├── ai │ │ └── ai_page.dart │ ├── bangumi │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── introduction │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ │ └── intro_detail.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── bangumi_panel.dart │ │ │ └── bangumu_card_v.dart │ ├── blacklist │ │ └── index.dart │ ├── contact │ │ └── contact_page.dart │ ├── danmaku │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── discover │ │ └── discover_page.dart │ ├── dlna │ │ └── index.dart │ ├── dynamics │ │ ├── controller.dart │ │ ├── detail │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ ├── index.dart │ │ ├── up_dynamic │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── route_panel.dart │ │ │ └── view.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── action_panel.dart │ │ │ ├── additional_panel.dart │ │ │ ├── article_panel.dart │ │ │ ├── author_panel.dart │ │ │ ├── content_panel.dart │ │ │ ├── dynamic_panel.dart │ │ │ ├── forward_panel.dart │ │ │ ├── live_panel.dart │ │ │ ├── live_rcmd_panel.dart │ │ │ ├── pic_panel.dart │ │ │ ├── rich_node_panel.dart │ │ │ ├── up_panel.dart │ │ │ └── video_panel.dart │ ├── emote │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── fan │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ ├── widgets │ │ │ ├── fan_item.dart │ │ │ └── no_login_panel.dart │ │ └── wrapper.dart │ ├── fav │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── fav_detail │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widget │ │ │ └── fav_video_card.dart │ ├── fav_edit │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── fav_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── follow │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── follow_item.dart │ │ │ ├── follow_list.dart │ │ │ └── owner_follow_list.dart │ ├── follow_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── history │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── history_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── home │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── app_bar.dart │ ├── hot │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── html │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── later │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── live │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── live_item.dart │ ├── live_room │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── bottom_control.dart │ ├── login │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── main │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── media │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── conis.dart │ │ │ ├── like.dart │ │ │ ├── profile.dart │ │ │ └── seasons.dart │ ├── member_archive │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_article │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_coin │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── member_dynamics │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_like │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── member_search │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── member_seasons │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── message │ │ ├── at │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ ├── controller.dart │ │ ├── like │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ ├── message_page.dart │ │ ├── reply │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ │ └── system │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ ├── mine │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── opus │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── text_helper.dart │ │ └── view.dart │ ├── rank │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── zone │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ └── view.dart │ ├── rcmd │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── read │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── search │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── hot_keyword.dart │ │ │ └── search_text.dart │ ├── search_panel │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── article_panel.dart │ │ │ ├── live_panel.dart │ │ │ ├── media_bangumi_panel.dart │ │ │ ├── user_panel.dart │ │ │ └── video_panel.dart │ ├── search_result │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── setting │ │ ├── controller.dart │ │ ├── extra_setting.dart │ │ ├── index.dart │ │ ├── pages │ │ │ ├── action_menu_set.dart │ │ │ ├── color_select.dart │ │ │ ├── display_mode.dart │ │ │ ├── font_size_select.dart │ │ │ ├── home_tabbar_set.dart │ │ │ ├── logs.dart │ │ │ ├── navigation_bar_set.dart │ │ │ ├── play_gesture_set.dart │ │ │ └── play_speed_set.dart │ │ ├── play_setting.dart │ │ ├── privacy_setting.dart │ │ ├── recommend_setting.dart │ │ ├── style_setting.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── select_dialog.dart │ │ │ ├── select_item.dart │ │ │ ├── slide_dialog.dart │ │ │ └── switch_item.dart │ ├── subscription │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widgets │ │ │ └── item.dart │ ├── subscription_detail │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widget │ │ │ └── sub_video_card.dart │ ├── video │ │ ├── README.md │ │ ├── detail │ │ │ ├── controller.dart │ │ │ ├── index.dart │ │ │ ├── introduction │ │ │ │ ├── controller.dart │ │ │ │ ├── index.dart │ │ │ │ ├── view.dart │ │ │ │ └── widgets │ │ │ │ │ ├── action_item.dart │ │ │ │ │ ├── action_row_item.dart │ │ │ │ │ ├── fav_panel.dart │ │ │ │ │ ├── group_panel.dart │ │ │ │ │ ├── intro_detail.dart │ │ │ │ │ ├── menu_row.dart │ │ │ │ │ ├── page_panel.dart │ │ │ │ │ ├── season_panel.dart │ │ │ │ │ └── staff_up_item.dart │ │ │ ├── related │ │ │ │ ├── controller.dart │ │ │ │ ├── index.dart │ │ │ │ └── view.dart │ │ │ ├── reply │ │ │ │ ├── controller.dart │ │ │ │ ├── index.dart │ │ │ │ ├── view.dart │ │ │ │ └── widgets │ │ │ │ │ ├── reply_item.dart │ │ │ │ │ ├── reply_save.dart │ │ │ │ │ └── zan.dart │ │ │ ├── reply_new │ │ │ │ ├── index.dart │ │ │ │ ├── toolbar_icon_button.dart │ │ │ │ └── view.dart │ │ │ ├── reply_reply │ │ │ │ ├── controller.dart │ │ │ │ ├── index.dart │ │ │ │ └── view.dart │ │ │ ├── view.dart │ │ │ └── widgets │ │ │ │ ├── ai_detail.dart │ │ │ │ ├── app_bar.dart │ │ │ │ ├── expandable_section.dart │ │ │ │ ├── header_control.dart │ │ │ │ ├── right_drawer.dart │ │ │ │ └── watch_later_list.dart │ │ └── video_page.dart │ ├── webview │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ ├── whisper │ │ ├── controller.dart │ │ ├── index.dart │ │ └── view.dart │ └── whisper_detail │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── view.dart │ │ └── widget │ │ └── chat_item.dart ├── plugin │ ├── pl_gallery │ │ ├── custom_dismissible.dart │ │ ├── hero_dialog_route.dart │ │ ├── index.dart │ │ ├── interactive_viewer_boundary.dart │ │ └── interactiveviewer_gallery.dart │ ├── pl_player │ │ ├── controller.dart │ │ ├── index.dart │ │ ├── models │ │ │ ├── bottom_control_type.dart │ │ │ ├── bottom_progress_behavior.dart │ │ │ ├── data_source.dart │ │ │ ├── data_status.dart │ │ │ ├── duration.dart │ │ │ ├── fullscreen_mode.dart │ │ │ ├── play_repeat.dart │ │ │ ├── play_speed.dart │ │ │ └── play_status.dart │ │ ├── utils.dart │ │ ├── utils │ │ │ └── fullscreen.dart │ │ ├── view.dart │ │ └── widgets │ │ │ ├── app_bar_ani.dart │ │ │ ├── backward_seek.dart │ │ │ ├── bottom_control.dart │ │ │ ├── common_btn.dart │ │ │ ├── control_bar.dart │ │ │ ├── forward_seek.dart │ │ │ └── play_pause_btn.dart │ ├── pl_popup │ │ └── index.dart │ └── pl_socket │ │ └── index.dart ├── router │ └── app_pages.dart ├── scripts │ └── build.sh ├── services │ ├── audio_handler.dart │ ├── audio_session.dart │ ├── disable_battery_opt.dart │ ├── loggeer.dart │ ├── service_locator.dart │ └── shutdown_timer_service.dart └── utils │ ├── app_scheme.dart │ ├── binary_writer.dart │ ├── cache_manage.dart │ ├── cookie.dart │ ├── danmaku.dart │ ├── data.dart │ ├── download.dart │ ├── drawer.dart │ ├── em.dart │ ├── event_bus.dart │ ├── extension.dart │ ├── feed_back.dart │ ├── global_data_cache.dart │ ├── highlight.dart │ ├── id_utils.dart │ ├── image_save.dart │ ├── live.dart │ ├── login.dart │ ├── main_stream.dart │ ├── proxy.dart │ ├── recommend_filter.dart │ ├── route_push.dart │ ├── storage.dart │ ├── subtitle.dart │ ├── url_utils.dart │ ├── utils.dart │ ├── video_utils.dart │ └── wbi_sign.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ └── CMakeLists.txt ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon_1024.png │ │ ├── app_icon_128.png │ │ ├── app_icon_16.png │ │ ├── app_icon_256.png │ │ ├── app_icon_32.png │ │ ├── app_icon_512.png │ │ └── app_icon_64.png │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter └── CMakeLists.txt └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 4b12645012342076800eb701bcdfe18f87da21cf 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 17 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 18 | - platform: android 19 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 20 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 21 | - platform: ios 22 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 23 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 24 | - platform: linux 25 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 26 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 27 | - platform: macos 28 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 29 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 30 | - platform: web 31 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 32 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 33 | - platform: windows 34 | create_revision: 4b12645012342076800eb701bcdfe18f87da21cf 35 | base_revision: 4b12645012342076800eb701bcdfe18f87da21cf 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/guozhigq/pilipala/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.guozhigq.pilipala 2 | 3 | // import io.flutter.embedding.android.FlutterActivity 4 | import com.ryanheise.audioservice.AudioServiceActivity; 5 | 6 | class MainActivity: AudioServiceActivity() { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-hdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-hdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-mdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-mdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xhdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxhdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi-v26/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxxhdpi-v26/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/drawable-xxxhdpi/ic_notification_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_baseline_forward_10_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_baseline_replay_10_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/fonts/Jura-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/fonts/Jura-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/fansCard.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/fonts/fansCard.ttf -------------------------------------------------------------------------------- /assets/images/ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/ai.png -------------------------------------------------------------------------------- /assets/images/big-vip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/big-vip.png -------------------------------------------------------------------------------- /assets/images/coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/coin.png -------------------------------------------------------------------------------- /assets/images/dm_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/dm_gray.png -------------------------------------------------------------------------------- /assets/images/dm_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/dm_white.png -------------------------------------------------------------------------------- /assets/images/live.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/live.gif -------------------------------------------------------------------------------- /assets/images/live.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/live.png -------------------------------------------------------------------------------- /assets/images/live/default_bg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/live/default_bg.webp -------------------------------------------------------------------------------- /assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/loading.gif -------------------------------------------------------------------------------- /assets/images/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/loading.png -------------------------------------------------------------------------------- /assets/images/logo/logo_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/logo/logo_android.png -------------------------------------------------------------------------------- /assets/images/logo/logo_android_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/logo/logo_android_2.png -------------------------------------------------------------------------------- /assets/images/logo/logo_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/logo/logo_ios.png -------------------------------------------------------------------------------- /assets/images/lv/lv0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv0.png -------------------------------------------------------------------------------- /assets/images/lv/lv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv1.png -------------------------------------------------------------------------------- /assets/images/lv/lv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv2.png -------------------------------------------------------------------------------- /assets/images/lv/lv3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv3.png -------------------------------------------------------------------------------- /assets/images/lv/lv4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv4.png -------------------------------------------------------------------------------- /assets/images/lv/lv5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv5.png -------------------------------------------------------------------------------- /assets/images/lv/lv6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/lv/lv6.png -------------------------------------------------------------------------------- /assets/images/noface.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/noface.jpeg -------------------------------------------------------------------------------- /assets/images/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/play.png -------------------------------------------------------------------------------- /assets/images/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/run-pokemon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/run-pokemon.gif -------------------------------------------------------------------------------- /assets/images/tv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/up_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/up_gray.png -------------------------------------------------------------------------------- /assets/images/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/video.png -------------------------------------------------------------------------------- /assets/images/video/danmu_close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/images/view.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /assets/images/view_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/view_gray.png -------------------------------------------------------------------------------- /assets/images/view_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/images/view_white.png -------------------------------------------------------------------------------- /assets/screenshots/174shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/174shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/510shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/510shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/850shots_so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/850shots_so.png -------------------------------------------------------------------------------- /assets/screenshots/bangumi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/bangumi.png -------------------------------------------------------------------------------- /assets/screenshots/bangumi_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/bangumi_detail.png -------------------------------------------------------------------------------- /assets/screenshots/dynamic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/dynamic.png -------------------------------------------------------------------------------- /assets/screenshots/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/home.png -------------------------------------------------------------------------------- /assets/screenshots/main_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/main_screen.png -------------------------------------------------------------------------------- /assets/screenshots/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/media.png -------------------------------------------------------------------------------- /assets/screenshots/member.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/member.png -------------------------------------------------------------------------------- /assets/screenshots/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/search.png -------------------------------------------------------------------------------- /assets/screenshots/set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/assets/screenshots/set.png -------------------------------------------------------------------------------- /change_log/1.0.0.0817.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | ### 初始版本 4 | + 直播、推荐、动态功能 5 | + 投稿、番剧播放功能 6 | + 播放器手势支持 7 | + 画质、音质、解码格式支持 8 | + 点赞、投币、收藏功能 9 | + 关注/取关、用户主页功能 10 | + 评论功能 11 | + 历史记录、稍后再看功能 -------------------------------------------------------------------------------- /change_log/1.0.1.0817.md: -------------------------------------------------------------------------------- 1 | ## 1.0.1 2 | 3 | ### 修复 4 | + 升级播放器依赖 5 | + android平台 AV1格式视频支持 6 | + 视频全屏功能 7 | 8 | -------------------------------------------------------------------------------- /change_log/1.0.10.1016.md: -------------------------------------------------------------------------------- 1 | ## 1.0.10 2 | 3 | ### 修复 4 | + 长按倍速抬起后未恢复默认倍速 -------------------------------------------------------------------------------- /change_log/1.0.11.1112.md: -------------------------------------------------------------------------------- 1 | ## 1.0.11 2 | 3 | ### 新功能 4 | + 适配了原生媒体通知栏 @Daydreamer-riri 5 | + 视频主题图标 @Daydreamer-riri 6 | + 关闭软件后自动画中画播放 7 | + UP主分组管理 8 | + md2样式底栏 9 | + 10 | 11 | 12 | ### 修复 13 | + 历史记录记忆播放 14 | + 部分类型视频连播 15 | + 播放速度选择框不支持返回手势 16 | + 播放速度选择框不支持返回手势 17 | + 视频播放速度总是显示1.0X 18 | + 评论页面计数错误 19 | + 退出视频还有声音 20 | 21 | 22 | ### 优化 23 | + 视频加载速度 24 | 25 | 更多更新日志可在Github上查看 26 | 问题反馈、功能建议请查看「关于」页面。 -------------------------------------------------------------------------------- /change_log/1.0.12.1114.md: -------------------------------------------------------------------------------- 1 | ## 1.0.12 2 | 3 | 4 | ### 修复 5 | + iOS端视频播放时没有声音 6 | + 超过6分钟弹幕不显示 7 | + 视频详情页网络异常 8 | 9 | 10 | 更多更新日志可在Github上查看 11 | 问题反馈、功能建议请查看「关于」页面。 -------------------------------------------------------------------------------- /change_log/1.0.13.1217.md: -------------------------------------------------------------------------------- 1 | ## 1.0.13 2 | 3 | 4 | ### 新功能 5 | + 视频详情页稍后再看 6 | + 发送弹幕 感谢@orz12 7 | + 消息展示 8 | + up主页显示获赞数 9 | + up主页显示合集 10 | + 视频详情页「ai总结」增加开关 11 | 12 | ### 修复 13 | + 首页推荐问题(需要重新登录) 14 | + 长按倍速逻辑 15 | + 视频详情页网络异常 16 | 17 | ### 优化 18 | + 设置面板样式 感谢@GuMengYu @KoolShow 19 | 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.14.1225.md: -------------------------------------------------------------------------------- 1 | ## 1.0.14 2 | 3 | 圣诞节快乐~ 🎉 4 | 5 | 大部分内容由@orz12提供,感谢👏 6 | 7 | ### 修复 8 | + 全屏弹幕消失 9 | + iOS全屏/退出全屏视频暂停 10 | + 个人主页关注状态 11 | + 视频合集向下滑动UI问题 12 | + 媒体库滑动底栏不隐藏 13 | + 个人主页动态加载问题 * 2 14 | + 未登录状态访问个人主页异常 15 | + 视频搜索标题特殊字符转义 16 | + iOS闪退 17 | + 消息页面夜间模式异常 18 | + 消息页面含有撤回消息时异常 19 | + 弹幕速度 20 | 21 | ### 优化 22 | + 全屏播放方案优化 23 | + 弹幕加载逻辑优化 24 | + 点赞、投币逻辑优化 25 | + 进度条及播放时间渲染优化 26 | 27 | 更多更新日志可在Github上查看 28 | 问题反馈、功能建议请查看「关于」页面。 29 | -------------------------------------------------------------------------------- /change_log/1.0.15.0101.md: -------------------------------------------------------------------------------- 1 | ## 1.0.15 2 | 3 | 元旦快乐~ 🎉 4 | 5 | ### 功能 6 | + 转发动态评论展示 7 | + 推荐、最热、收藏视频增肌日期显示 8 | 9 | ### 修复 10 | + 全屏播放相关问题 11 | + 评论区@用户展示问题 12 | + 登录状态闪退问题 13 | + pip意外触发问题 14 | + 动态页tab切换样式问题 15 | 16 | ### 优化 17 | + 首页默认使用web端推荐 18 | + 取消iOS路由切换效果 19 | + 视频分享中添加Up主 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.16.0102.md: -------------------------------------------------------------------------------- 1 | ## 1.0.16 2 | 3 | 4 | ### 功能 5 | + toast 背景支持透明度调节 6 | 7 | ### 修复 8 | + web端推荐未展示【已关注】 9 | + up主动态页异常 10 | + 未打开自动播放时,视频详情页异常 11 | + 视频暂停状态取消自动ip 12 | 13 | 14 | 更多更新日志可在Github上查看 15 | 问题反馈、功能建议请查看「关于」页面。 16 | -------------------------------------------------------------------------------- /change_log/1.0.17.0125.md: -------------------------------------------------------------------------------- 1 | ## 1.0.17 2 | 3 | 4 | ### 功能 5 | + 视频全屏时隐藏进度条 6 | + 动态内容增加投稿跳转 7 | + 未开启自动播放时点击封面播放 8 | + 弹幕发送标识 9 | + 定时关闭 10 | + 推荐视频卡片拉黑up功能 11 | + 首页tabbar编辑排序 12 | 13 | ### 修复 14 | + 连续跳转搜索页未刷新 15 | + 搜索结果为空时页面异常 16 | + 评论区链接解析 17 | + 视频全屏状态栏背景色 18 | + 私信对话气泡位置 19 | + 设置up关注分组样式 20 | + 每次推荐请求数据相同 21 | + iOS代理网络异常 22 | + 双击切换播放状态无声 23 | + 设置自定义倍速白屏 24 | + 免登录查看1080p 25 | 26 | ### 优化 27 | + 首页web端推荐观看数展示 28 | + 首页web端推荐接口更新 29 | + 首页样式 30 | + 搜索页跳转 31 | + 弹幕资源优化 32 | + 图片渲染占用内存优化(部分) 33 | + 两次返回退出应用 34 | + schame 补充 35 | 36 | 37 | 38 | 更多更新日志可在Github上查看 39 | 问题反馈、功能建议请查看「关于」页面。 40 | -------------------------------------------------------------------------------- /change_log/1.0.18.0130.md: -------------------------------------------------------------------------------- 1 | ## 1.0.18 2 | 3 | 4 | ### 功能 5 | 6 | 7 | ### 修复 8 | 9 | 10 | ### 优化 11 | 12 | 13 | 14 | 15 | 更多更新日志可在Github上查看 16 | 问题反馈、功能建议请查看「关于」页面。 17 | -------------------------------------------------------------------------------- /change_log/1.0.19.0131.md: -------------------------------------------------------------------------------- 1 | ## 1.0.19 2 | 3 | 4 | ### 修复 5 | + 视频404、评论加载错误 6 | + bvav转换 7 | 8 | ### 优化 9 | + 视频详情页内存占用 10 | 11 | 12 | 13 | 14 | 更多更新日志可在Github上查看 15 | 问题反馈、功能建议请查看「关于」页面。 16 | -------------------------------------------------------------------------------- /change_log/1.0.2.0819.md: -------------------------------------------------------------------------------- 1 | ## 1.0.2 2 | 3 | ### 新功能 4 | + 自动检查更新 5 | + 封面图片保存 6 | + 动态跳转番剧 7 | + 历史记录番剧记忆播放 8 | + 一键清空稍后再看 9 | 10 | ### 修复 11 | + 切换分P cid未切换 12 | + cookie存储问题 13 | + 登录/退出登录问题 14 | 15 | ### 优化 16 | + 页面空/异常状态样式 17 | + 退出登录提示 18 | + 请求节流 19 | + 全屏播放 -------------------------------------------------------------------------------- /change_log/1.0.20.0303.md: -------------------------------------------------------------------------------- 1 | ## 1.0.20 2 | 3 | 4 | ### 功能 5 | + 评论区增加表情 6 | + 首页渐变背景开关 7 | + 媒体库显示「我的订阅」 8 | + 评论区链接解析 9 | + 默认启动页设置 10 | 11 | ### 修复 12 | + 评论区内容重复 13 | + pip相关问题 14 | + 播放多p视频评论不刷新 15 | + 视频评论翻页重复 16 | 17 | ### 优化 18 | + url scheme优化 19 | + 图片预览放大 20 | + 图片加载速度 21 | + 视频评论区复制 22 | + 全屏显示视频标题 23 | + 网络异常处理 24 | 25 | 26 | 27 | 28 | 29 | 30 | 更多更新日志可在Github上查看 31 | 问题反馈、功能建议请查看「关于」页面。 32 | -------------------------------------------------------------------------------- /change_log/1.0.21.0306.md: -------------------------------------------------------------------------------- 1 | ## 1.0.21 2 | 3 | ### 修复 4 | + 推荐视频全屏问题 5 | + 番剧全屏播放时灰屏问题 6 | + 评论回调导致页面卡死问题 7 | 8 | 更多更新日志可在Github上查看 9 | 问题反馈、功能建议请查看「关于」页面。 10 | -------------------------------------------------------------------------------- /change_log/1.0.22.0430.md: -------------------------------------------------------------------------------- 1 | ## 1.0.22 2 | 3 | ### 功能 4 | + 字幕 5 | + 全屏时选集 6 | + 动态转发 7 | + 评论视频并转发 8 | + 收藏夹删除 9 | + 合集显示封面 10 | + 底部导航栏编辑、排序功能 11 | + 历史记录进度条展示 12 | + 直播画质切换 13 | + 排行榜功能 14 | + 视频详情页推荐视频开关 15 | + 显示联合投稿up 16 | 17 | ### 修复 18 | + 收藏夹个数错误 19 | + 封面保存权限问题 20 | + 合集最后1p未展示 21 | + up主页关注按钮触发灰屏 22 | 23 | ### 优化 24 | + 视频简介查看逻辑 25 | 26 | 更多更新日志可在Github上查看 27 | 问题反馈、功能建议请查看「关于」页面。 28 | -------------------------------------------------------------------------------- /change_log/1.0.23.0504.md: -------------------------------------------------------------------------------- 1 | ## 1.0.23 2 | 3 | ### 功能 4 | + 封面下载 5 | 6 | 7 | ### 修复 8 | + 全屏问题 9 | + 视频播放器灰屏问题 10 | + 评论区点击区域问题 11 | 12 | 13 | 更多更新日志可在Github上查看 14 | 问题反馈、功能建议请查看「关于」页面。 15 | -------------------------------------------------------------------------------- /change_log/1.0.23.0505.md: -------------------------------------------------------------------------------- 1 | ## 1.0.23 2 | 3 | ### 功能 4 | + 封面下载 5 | 6 | 7 | ### 修复 8 | + 全屏问题 9 | + 视频播放器灰屏问题 10 | + 评论区点击区域问题 11 | + 动态详情跳转异常问题 12 | 13 | 14 | 更多更新日志可在Github上查看 15 | 问题反馈、功能建议请查看「关于」页面。 16 | -------------------------------------------------------------------------------- /change_log/1.0.24.0626.md: -------------------------------------------------------------------------------- 1 | ## 1.0.24 2 | 3 | ### 功能 4 | + 私信功能 5 | + 回复我的、收到的赞查看 6 | + 新的登录方式 7 | + 全屏选集 8 | + 一键三连 9 | + 按分区搜索 10 | 11 | ### 优化 12 | + 页面跳转动画 13 | + 评论区跳转 14 | 15 | ### 修复 16 | + 音画不同步问题 17 | + 分集字幕未同步 18 | + 多语言字幕 19 | + 弹幕设置未生效 20 | 21 | 22 | 更多更新日志可在Github上查看 23 | 问题反馈、功能建议请查看「关于」页面。 24 | -------------------------------------------------------------------------------- /change_log/1.0.25.1010.md: -------------------------------------------------------------------------------- 1 | ## 1.0.25 2 | 3 | ### 功能 4 | + 直播弹幕 5 | + 稍后再看、收藏夹播放全部 6 | + 收藏夹新建、编辑 7 | + 评论删除 8 | + 评论保存为图片 9 | + 动态页滑动切换up 10 | + up投稿筛选充电视频 11 | + 直播tab展示关注up 12 | + up主页专栏展示 13 | 14 | ### 优化 15 | + 视频详情页一键三连 16 | + 动态页标识充电视频 17 | + 播放器亮度、音量调整百分比展示 18 | + 封面预览时视频标题可复制 19 | + 竖屏直播布局 20 | + 图片预览 21 | + 专栏渲染优化 22 | + 私信图片查看 23 | 24 | ### 修复 25 | + 收藏夹点击异常 26 | + 搜索up异常 27 | + 系统通知已读异常 28 | + [赞了我的]展示错误 29 | + 部分up合集无法打开 30 | + 切换合集视频投币个数未重置 31 | + 搜索条件筛选面板无法滚动 32 | + 部分机型导航条未沉浸 33 | + 专栏图片渲染问题 34 | + 专栏浏览历史记录 35 | + 直播间历史记录 36 | 37 | 38 | 更多更新日志可在Github上查看 39 | 问题反馈、功能建议请查看「关于」页面。 40 | -------------------------------------------------------------------------------- /change_log/1.0.3.0821.md: -------------------------------------------------------------------------------- 1 | ## 1.0.3 2 | 3 | 建议卸载1.0.2版本,重新安装 4 | ### 新功能 5 | + 底部播放进度条设置 6 | + 复制图片链接 7 | 8 | 9 | ### 修复 10 | + 用户数据格式修改 11 | + video Fit 12 | + 没有audio 资源的视频异常 13 | + 评论区域图片无法点击 14 | + 视频进度条拖动问题 15 | 16 | ### 优化 17 | + 页面空/异常状态样式 18 | + 部分页面样式 19 | + 图片预览页面样式 -------------------------------------------------------------------------------- /change_log/1.0.4.0822.md: -------------------------------------------------------------------------------- 1 | ## 1.0.4 2 | 3 | ### 新功能 4 | + 热搜刷新 5 | + 视频搜索排序、筛选 6 | + app字体大小自定义 7 | + app主题色自定义 8 | + 「课堂」类动态渲染 9 | 10 | 11 | ### 修复 12 | + 搜索词联想richText渲染异常 13 | + 部分动态点赞异常 14 | + 默认视频解码格式 15 | + 搜索页面返回搜索词未清空 16 | + 动态详情评论加载异常 17 | + 动态页面下拉刷新数据异常 18 | 19 | ### 优化 20 | + 一些样式修改 21 | + 取消热搜词缓存 -------------------------------------------------------------------------------- /change_log/1.0.5.0826.md: -------------------------------------------------------------------------------- 1 | ## 1.0.5 2 | 3 | 主要是bug修复跟一部分小功能,弹幕功能需要下一版。 4 | 问题反馈请前往QQ频道或提交issues。 5 | 感谢🙏酷友「无力感*」「斤斤计较呀」「Pseudopamine」 6 | 7 | ### 新功能 8 | + 高帧率支持 9 | + 默认评论排序设置 10 | + 默认动态类别设置 11 | + 动态合集查看 12 | + 同时观看人数 13 | + iOS路由切换效果 14 | 15 | 16 | ### 修复 17 | + 收藏夹翻页 18 | + 首页搜索框频繁点击消失 19 | + 评论排序切换空白 20 | + 快速返回首页 21 | + 重复进入个人中心页面数据未刷新 22 | + 动态goods数据异常 23 | + 大会员切换番剧 24 | + 高画质codes匹配 25 | 26 | 27 | ### 优化 28 | + 倍速选择 29 | + 播放器亮度记忆 30 | + 下载对应版本apk -------------------------------------------------------------------------------- /change_log/1.0.6.0902.md: -------------------------------------------------------------------------------- 1 | ## 1.0.6 2 | 3 | 问题反馈、功能建议请查看「关于」页面。 4 | 5 | ### 新功能 6 | + 首页单列布局 7 | + 首页推荐展示播放量、弹幕数 8 | + 简单弹幕功能实现(持续开发中...) 9 | + 评论区搜索关键词开关 issues#46 10 | + 热搜榜隐藏功能 issues#35 11 | + 自动全屏 issues#37 12 | + 快速收藏功能 13 | + 双击快进/快退开关 14 | + 评论链接跳转视频 15 | + 支持移除单个稍后再看 16 | + app scheme外链跳转 17 | 18 | 19 | ### 修复 20 | + 杜比、无损音频切换 21 | + 收藏夹展示 issues#42 22 | + 搜索建议次 issues#47 23 | 24 | 25 | ### 优化 26 | + 倍速选择优化 27 | + 导航条沉浸 28 | + 取消Hero动画 29 | + 视频锁定逻辑 30 | + 登录逻辑优化 31 | + 图片预览样式 32 | + +评论区用户点击范围 33 | + 关注、粉丝页面优化 34 | + 关闭自动播放时播放器初始化逻辑 -------------------------------------------------------------------------------- /change_log/1.0.7.0908.md: -------------------------------------------------------------------------------- 1 | ## 1.0.7 2 | 3 | 默认倍速、直播弹幕、专栏等功能开发中 4 | 5 | ### 新功能 6 | + 弹幕设置、屏蔽功能 7 | + 不是很完美的后台播放功能 8 | + 不是很完美的画中画(pip)功能(Android端) 9 | 10 | ### 修复 11 | + 动态页面加载异常 12 | + 网络异常时页面空白 13 | + 竖屏全屏状态栏问题 14 | + iOS端代理请求异常 15 | 16 | ### 优化 17 | + 图片预览 18 | + 全屏播放时自动旋转 19 | + 转发内容增加视频标题 20 | 21 | 更多更新日志可在Github上查看 22 | 问题反馈、功能建议请查看「关于」页面。 23 | -------------------------------------------------------------------------------- /change_log/1.0.8.0917.md: -------------------------------------------------------------------------------- 1 | ## 1.0.8 2 | 3 | 直播弹幕、循环播放等功能开发中 4 | 5 | ### 新功能 6 | + 用户拉黑功能 7 | + gif图片保存 8 | + 删除已看历史记录 9 | 10 | ### 修复 11 | + 弹幕数量较少 12 | + 弹幕屏蔽设置自动记忆 13 | + 动态页面渲染 14 | + 用户主页数据错乱 15 | + 大家都在搜空白 16 | + 默认自动全屏,顶部操作栏丢失 17 | 18 | 19 | ### 优化 20 | + 全屏状态栏区域显示优化 21 | + 图片保存至PiliPala文件夹 22 | 23 | 更多更新日志可在Github上查看 24 | 问题反馈、功能建议请查看「关于」页面。 25 | -------------------------------------------------------------------------------- /change_log/1.0.9.1015.md: -------------------------------------------------------------------------------- 1 | ## 1.0.9 2 | 3 | 4 | ### 新功能 5 | + 自定义倍速、默认倍速 6 | + 历史记录搜索 7 | + 收藏夹搜索 8 | + 历史记录多选删除 9 | + 视频循环播放 10 | + 免登录看1080P 11 | + 评论区视频链接跳转 12 | + up主分组 13 | + up主投稿搜索 14 | 15 | ### 修复 16 | + 搜索视频标题乱码 17 | + 屏幕帧率 18 | + 动态页面渲染 19 | 20 | 21 | 22 | ### 优化 23 | + 快进手势 24 | + 视频简介链接匹配 25 | + 视频全屏时安全区域 26 | 27 | 更多更新日志可在Github上查看 28 | 问题反馈、功能建议请查看「关于」页面。 29 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | PiliPala is a third-party Bilibili client developed in Flutter. 2 | 3 | Top Features: 4 | 5 | * List of recommended videos 6 | * List of hottest videos 7 | * Popular live streams 8 | * List of bangumis 9 | * Block videos from blacklisted users 10 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/en-US/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A third-party Bilibili client developed in Flutter 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | PiliPala 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/changelogs/2001.txt: -------------------------------------------------------------------------------- 1 | 修复 2 | 3 | * 全屏弹幕消失 4 | * iOS 全屏/退出全屏视频暂停 5 | * 个人主页关注状态 6 | * 视频合集向下滑动UI问题 7 | * 媒体库滑动底栏不隐藏 8 | * 个人主页动态加载问题 * 2 9 | * 未登录状态访问个人主页异常 10 | * 视频搜索标题特殊字符转义 11 | * iOS 闪退 12 | * 消息页面夜间模式异常 13 | * 消息页面含有撤回消息时异常 14 | * 弹幕速度 15 | 16 | 优化 17 | 18 | * 全屏播放方案优化 19 | * 弹幕加载逻辑优化 20 | * 点赞、投币逻辑优化 21 | * 进度条及播放时间渲染优化 22 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/full_description.txt: -------------------------------------------------------------------------------- 1 | PiliPala 是使用 Flutter 开发的 BiliBili 第三方客户端。 2 | 3 | 主要功能: 4 | 5 | * 推荐视频列表 (app 端) 6 | * 最热视频列表 7 | * 热门直播 8 | * 番剧列表 9 | * 屏蔽黑名单内用户视频 10 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/zh-CN/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/zh-CN/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/short_description.txt: -------------------------------------------------------------------------------- 1 | 使用 Flutter 开发的 BiliBili 第三方客户端 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/zh-CN/title.txt: -------------------------------------------------------------------------------- 1 | PiliPala 2 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | target.build_configurations.each do |config| 41 | deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] 42 | if !deployment_target.nil? && !deployment_target.empty? && deployment_target.to_f < 12.0 43 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import AVFoundation 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GeneratedPluginRegistrant.register(with: self) 12 | 13 | // 设置音频会话类别,确保在静音模式下播放音频 14 | do { 15 | try AVAudioSession.sharedInstance().setCategory(.playback, options: [.duckOthers]) 16 | } catch { 17 | print("Failed to set audio session category: \(error)") 18 | } 19 | 20 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 21 | } 22 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/common/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StyleString { 4 | static const double cardSpace = 8; 5 | static const double safeSpace = 12; 6 | static BorderRadius mdRadius = BorderRadius.circular(10); 7 | static const Radius imgRadius = Radius.circular(10); 8 | static const double aspectRatio = 16 / 10; 9 | } 10 | 11 | class Constants { 12 | // 27eb53fc9058f8c3 移动端 Android 13 | // 4409e2ce8ffd12b8 TV端 14 | static const String appKey = '4409e2ce8ffd12b8'; 15 | // 59b43e04ad6965f34319062b478f83dd TV端 16 | static const String appSec = '59b43e04ad6965f34319062b478f83dd'; 17 | static const String thirdSign = '04224646d1fea004e79606d3b038c84a'; 18 | static const String thirdApi = 19 | 'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png'; 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/widgets/appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppBarWidget extends StatelessWidget implements PreferredSizeWidget { 4 | const AppBarWidget({ 5 | required this.child, 6 | required this.controller, 7 | required this.visible, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final PreferredSizeWidget child; 12 | final AnimationController controller; 13 | final bool visible; 14 | 15 | @override 16 | Size get preferredSize => child.preferredSize; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | visible ? controller.reverse() : controller.forward(); 21 | return SlideTransition( 22 | position: Tween( 23 | begin: Offset.zero, 24 | end: const Offset(0, -1), 25 | ).animate(CurvedAnimation( 26 | parent: controller, 27 | curve: Curves.easeInOutBack, 28 | )), 29 | child: child, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/common/widgets/content_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ContentContainer extends StatelessWidget { 4 | final Widget? contentWidget; 5 | final Widget? bottomWidget; 6 | final bool isScrollable; 7 | final Clip? childClipBehavior; 8 | 9 | const ContentContainer( 10 | {Key? key, 11 | this.contentWidget, 12 | this.bottomWidget, 13 | this.isScrollable = true, 14 | this.childClipBehavior}) 15 | : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return LayoutBuilder( 20 | builder: (BuildContext context, BoxConstraints constraints) { 21 | return SingleChildScrollView( 22 | clipBehavior: childClipBehavior ?? Clip.hardEdge, 23 | physics: isScrollable ? null : const NeverScrollableScrollPhysics(), 24 | child: ConstrainedBox( 25 | constraints: constraints.copyWith( 26 | minHeight: constraints.maxHeight, 27 | maxHeight: double.infinity, 28 | ), 29 | child: IntrinsicHeight( 30 | child: Column( 31 | children: [ 32 | if (contentWidget != null) 33 | Expanded( 34 | child: contentWidget!, 35 | ) 36 | else 37 | const Spacer(), 38 | if (bottomWidget != null) bottomWidget!, 39 | ], 40 | ), 41 | ), 42 | ), 43 | ); 44 | }, 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/common/widgets/custom_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'package:pilipala/utils/storage.dart'; 4 | 5 | Box setting = GStrorage.setting; 6 | 7 | class CustomToast extends StatelessWidget { 8 | const CustomToast({super.key, required this.msg}); 9 | 10 | final String msg; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final double toastOpacity = 15 | setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0) as double; 16 | return Container( 17 | margin: 18 | EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 30), 19 | padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10), 20 | decoration: BoxDecoration( 21 | color: Theme.of(context) 22 | .colorScheme 23 | .primaryContainer 24 | .withOpacity(toastOpacity), 25 | borderRadius: BorderRadius.circular(20), 26 | ), 27 | child: Text( 28 | msg, 29 | style: TextStyle( 30 | fontSize: 13, 31 | color: Theme.of(context).colorScheme.primary, 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/common/widgets/no_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | 4 | class NoData extends StatelessWidget { 5 | const NoData({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return SliverToBoxAdapter( 10 | child: SizedBox( 11 | height: 400, 12 | child: Column( 13 | crossAxisAlignment: CrossAxisAlignment.center, 14 | mainAxisAlignment: MainAxisAlignment.center, 15 | children: [ 16 | SvgPicture.asset( 17 | "assets/images/error.svg", 18 | height: 200, 19 | ), 20 | const SizedBox(height: 20), 21 | Text( 22 | '没有数据', 23 | textAlign: TextAlign.center, 24 | style: Theme.of(context).textTheme.titleSmall, 25 | ), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/common/widgets/sliver_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { 4 | SliverHeaderDelegate({required this.height, required this.child}); 5 | 6 | final double height; 7 | final Widget child; 8 | 9 | @override 10 | Widget build( 11 | BuildContext context, double shrinkOffset, bool overlapsContent) { 12 | return child; 13 | } 14 | 15 | @override 16 | double get maxExtent => height; 17 | 18 | @override 19 | double get minExtent => height; 20 | 21 | @override 22 | bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => 23 | true; 24 | } 25 | -------------------------------------------------------------------------------- /lib/common/widgets/stat/danmu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pilipala/utils/utils.dart'; 3 | 4 | class StatDanMu extends StatelessWidget { 5 | final String? theme; 6 | final dynamic danmu; 7 | final String? size; 8 | 9 | const StatDanMu({Key? key, this.theme = 'gray', this.danmu, this.size}) 10 | : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | Map colorObject = { 15 | 'white': Colors.white, 16 | 'gray': Theme.of(context).colorScheme.outline, 17 | 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), 18 | }; 19 | Color color = colorObject[theme]!; 20 | return StatIconText( 21 | icon: Icons.subtitles_outlined, 22 | text: Utils.numFormat(danmu!), 23 | color: color, 24 | size: size, 25 | ); 26 | } 27 | } 28 | 29 | class StatIconText extends StatelessWidget { 30 | final IconData icon; 31 | final String text; 32 | final Color color; 33 | final String? size; 34 | 35 | const StatIconText({ 36 | Key? key, 37 | required this.icon, 38 | required this.text, 39 | required this.color, 40 | this.size, 41 | }) : super(key: key); 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Row( 46 | children: [ 47 | Icon( 48 | icon, 49 | size: 14, 50 | color: color, 51 | ), 52 | const SizedBox(width: 2), 53 | Text( 54 | text, 55 | style: TextStyle( 56 | fontSize: size == 'medium' ? 12 : 11, 57 | color: color, 58 | ), 59 | ), 60 | ], 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/common/widgets/stat/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pilipala/utils/utils.dart'; 3 | 4 | class StatView extends StatelessWidget { 5 | final String? theme; 6 | final dynamic view; 7 | final String? size; 8 | 9 | const StatView({ 10 | Key? key, 11 | this.theme = 'gray', 12 | this.view, 13 | this.size, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | Map colorObject = { 19 | 'white': Colors.white, 20 | 'gray': Theme.of(context).colorScheme.outline, 21 | 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), 22 | }; 23 | Color color = colorObject[theme]!; 24 | return StatIconText( 25 | icon: Icons.play_circle_outlined, 26 | text: Utils.numFormat(view!), 27 | color: color, 28 | size: size, 29 | ); 30 | } 31 | } 32 | 33 | class StatIconText extends StatelessWidget { 34 | final IconData icon; 35 | final String text; 36 | final Color color; 37 | final String? size; 38 | 39 | const StatIconText({ 40 | Key? key, 41 | required this.icon, 42 | required this.text, 43 | required this.color, 44 | this.size, 45 | }) : super(key: key); 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Row( 50 | children: [ 51 | Icon( 52 | icon, 53 | size: 13, 54 | color: color, 55 | ), 56 | const SizedBox(width: 2), 57 | Text( 58 | text, 59 | style: TextStyle( 60 | fontSize: size == 'medium' ? 12 : 11, 61 | color: color, 62 | ), 63 | ), 64 | ], 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/http/bangumi.dart: -------------------------------------------------------------------------------- 1 | import '../models/bangumi/list.dart'; 2 | import 'index.dart'; 3 | 4 | class BangumiHttp { 5 | static Future bangumiList({int? page}) async { 6 | var res = await Request().get(Api.bangumiList, data: {'page': page}); 7 | if (res.data['code'] == 0) { 8 | return { 9 | 'status': true, 10 | 'data': BangumiListDataModel.fromJson(res.data['data']) 11 | }; 12 | } else { 13 | return { 14 | 'status': false, 15 | 'data': [], 16 | 'msg': res.data['message'], 17 | }; 18 | } 19 | } 20 | 21 | static Future bangumiFollow({int? mid}) async { 22 | var res = await Request().get(Api.bangumiFollow, data: {'vmid': mid}); 23 | if (res.data['code'] == 0) { 24 | return { 25 | 'status': true, 26 | 'data': BangumiListDataModel.fromJson(res.data['data']) 27 | }; 28 | } else { 29 | return { 30 | 'status': false, 31 | 'data': [], 32 | 'msg': res.data['message'], 33 | }; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/http/black.dart: -------------------------------------------------------------------------------- 1 | import '../models/user/black.dart'; 2 | import 'index.dart'; 3 | 4 | class BlackHttp { 5 | static Future blackList({required int pn, int? ps}) async { 6 | var res = await Request().get(Api.blackLst, data: { 7 | 'pn': pn, 8 | 'ps': ps ?? 50, 9 | 're_version': 0, 10 | 'jsonp': 'jsonp', 11 | 'csrf': await Request.getCsrf(), 12 | }); 13 | if (res.data['code'] == 0) { 14 | return { 15 | 'status': true, 16 | 'data': BlackListDataModel.fromJson(res.data['data']) 17 | }; 18 | } else { 19 | return { 20 | 'status': false, 21 | 'data': [], 22 | 'msg': res.data['message'], 23 | }; 24 | } 25 | } 26 | 27 | // 移除黑名单 28 | static Future removeBlack({required int fid}) async { 29 | var res = await Request().post( 30 | Api.removeBlack, 31 | data: { 32 | 'act': 6, 33 | 'csrf': await Request.getCsrf(), 34 | 'fid': fid, 35 | 'jsonp': 'jsonp', 36 | 're_src': 116, 37 | }, 38 | ); 39 | if (res.data['code'] == 0) { 40 | return { 41 | 'status': true, 42 | 'data': [], 43 | 'msg': '操作成功', 44 | }; 45 | } else { 46 | return { 47 | 'status': false, 48 | 'data': [], 49 | 'msg': res.data['message'], 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/http/common.dart: -------------------------------------------------------------------------------- 1 | import 'index.dart'; 2 | 3 | class CommonHttp { 4 | static Future unReadDynamic() async { 5 | var res = await Request().get(Api.getUnreadDynamic, 6 | data: {'alltype_offset': 0, 'video_offset': '', 'article_offset': 0}); 7 | if (res.data['code'] == 0) { 8 | return {'status': true, 'data': res.data['data']['dyn_basic_infos']}; 9 | } else { 10 | return { 11 | 'status': false, 12 | 'data': [], 13 | 'msg': res.data['message'], 14 | }; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/http/constants.dart: -------------------------------------------------------------------------------- 1 | class HttpString { 2 | static const String baseUrl = 'https://www.bilibili.com'; 3 | static const String apiBaseUrl = 'https://api.bilibili.com'; 4 | static const String tUrl = 'https://api.vc.bilibili.com'; 5 | static const String appBaseUrl = 'https://app.bilibili.com'; 6 | static const String liveBaseUrl = 'https://api.live.bilibili.com'; 7 | static const String passBaseUrl = 'https://passport.bilibili.com'; 8 | static const String messageBaseUrl = 'https://message.bilibili.com'; 9 | static const String bangumiBaseUrl = 'https://bili.meark.me'; 10 | static const List validateStatusCodes = [ 11 | 302, 12 | 304, 13 | 307, 14 | 400, 15 | 401, 16 | 403, 17 | 404, 18 | 405, 19 | 409, 20 | 412, 21 | 500, 22 | 503, 23 | 504, 24 | 509, 25 | 616, 26 | 617, 27 | 625, 28 | 626, 29 | 628, 30 | 629, 31 | 632, 32 | 643, 33 | 650, 34 | 652, 35 | 658, 36 | 662, 37 | 688, 38 | 689, 39 | 701, 40 | 799, 41 | 8888 42 | ]; 43 | } 44 | -------------------------------------------------------------------------------- /lib/http/fan.dart: -------------------------------------------------------------------------------- 1 | import '../models/fans/result.dart'; 2 | import 'index.dart'; 3 | 4 | class FanHttp { 5 | static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async { 6 | var res = await Request().get(Api.fans, data: { 7 | 'vmid': vmid, 8 | 'pn': pn, 9 | 'ps': ps, 10 | 'order': 'desc', 11 | 'order_type': orderType, 12 | }); 13 | if (res.data['code'] == 0) { 14 | return {'status': true, 'data': FansDataModel.fromJson(res.data['data'])}; 15 | } else { 16 | return { 17 | 'status': false, 18 | 'data': [], 19 | 'msg': res.data['message'], 20 | }; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/http/fav.dart: -------------------------------------------------------------------------------- 1 | import 'index.dart'; 2 | 3 | class FavHttp { 4 | /// 编辑收藏夹 5 | static Future editFolder({ 6 | required String title, 7 | required String intro, 8 | required String mediaId, 9 | String? cover, 10 | int? privacy, 11 | }) async { 12 | var res = await Request().post( 13 | Api.editFavFolder, 14 | data: { 15 | 'title': title, 16 | 'intro': intro, 17 | 'media_id': mediaId, 18 | 'cover': cover ?? '', 19 | 'privacy': privacy ?? 0, 20 | 'csrf': await Request.getCsrf(), 21 | }, 22 | ); 23 | if (res.data['code'] == 0) { 24 | return { 25 | 'status': true, 26 | 'data': res.data['data'], 27 | }; 28 | } else { 29 | return { 30 | 'status': false, 31 | 'data': [], 32 | 'msg': res.data['message'], 33 | }; 34 | } 35 | } 36 | 37 | /// 新建收藏夹 38 | static Future addFolder({ 39 | required String title, 40 | required String intro, 41 | String? cover, 42 | int? privacy, 43 | }) async { 44 | var res = await Request().post( 45 | Api.addFavFolder, 46 | data: { 47 | 'title': title, 48 | 'intro': intro, 49 | 'cover': cover ?? '', 50 | 'privacy': privacy ?? 0, 51 | 'csrf': await Request.getCsrf(), 52 | }, 53 | ); 54 | if (res.data['code'] == 0) { 55 | return { 56 | 'status': true, 57 | 'data': res.data['data'], 58 | }; 59 | } else { 60 | return { 61 | 'status': false, 62 | 'data': [], 63 | 'msg': res.data['message'], 64 | }; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/http/follow.dart: -------------------------------------------------------------------------------- 1 | import '../models/follow/result.dart'; 2 | import 'index.dart'; 3 | 4 | class FollowHttp { 5 | static Future followings( 6 | {int? vmid, int? pn, int? ps, String? orderType}) async { 7 | var res = await Request().get(Api.followings, data: { 8 | 'vmid': vmid, 9 | 'pn': pn, 10 | 'ps': ps, 11 | 'order': 'desc', 12 | 'order_type': orderType, 13 | }); 14 | if (res.data['code'] == 0) { 15 | return { 16 | 'status': true, 17 | 'data': FollowDataModel.fromJson(res.data['data']) 18 | }; 19 | } else { 20 | return { 21 | 'status': false, 22 | 'data': [], 23 | 'msg': res.data['message'], 24 | }; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/http/index.dart: -------------------------------------------------------------------------------- 1 | export 'api.dart'; 2 | export 'init.dart'; 3 | -------------------------------------------------------------------------------- /lib/models/common/business_type.dart: -------------------------------------------------------------------------------- 1 | enum BusinessType { 2 | // 普通视频 3 | archive, 4 | // 剧集(番剧 / 影视) 5 | pgc, 6 | // 直播 7 | live, 8 | // 文章 9 | articleList, 10 | // 文章 11 | article, 12 | hiddenDurationType, 13 | showBadge 14 | } 15 | 16 | extension BusinessTypeExtension on BusinessType { 17 | String get type => 18 | ['archive', 'pgc', 'live', 'article-list', 'article'][index]; 19 | // 隐藏时长 20 | List get hiddenDurationType => ['live', 'article-list', 'article']; 21 | // 右上 22 | List get showBadge => ['pgc', 'article-list', 'article']; 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/common/color_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final List> colorThemeTypes = [ 4 | {'color': const Color.fromARGB(255, 92, 182, 123), 'label': '默认绿'}, 5 | {'color': Colors.pink, 'label': '粉红色'}, 6 | {'color': Colors.red, 'label': '红色'}, 7 | {'color': Colors.orange, 'label': '橙色'}, 8 | {'color': Colors.amber, 'label': '琥珀色'}, 9 | {'color': Colors.yellow, 'label': '黄色'}, 10 | {'color': Colors.lime, 'label': '酸橙色'}, 11 | {'color': Colors.lightGreen, 'label': '浅绿色'}, 12 | {'color': Colors.green, 'label': '绿色'}, 13 | {'color': Colors.teal, 'label': '青色'}, 14 | {'color': Colors.cyan, 'label': '蓝绿色'}, 15 | {'color': Colors.lightBlue, 'label': '浅蓝色'}, 16 | {'color': Colors.blue, 'label': '蓝色'}, 17 | {'color': Colors.indigo, 'label': '靛蓝色'}, 18 | {'color': Colors.purple, 'label': '紫色'}, 19 | {'color': Colors.deepPurple, 'label': '深紫色'}, 20 | {'color': Colors.blueGrey, 'label': '蓝灰色'}, 21 | {'color': Colors.brown, 'label': '棕色'}, 22 | {'color': Colors.grey, 'label': '灰色'}, 23 | ]; 24 | -------------------------------------------------------------------------------- /lib/models/common/dynamic_badge_mode.dart: -------------------------------------------------------------------------------- 1 | enum DynamicBadgeMode { hidden, point, number } 2 | 3 | extension DynamicBadgeModeDesc on DynamicBadgeMode { 4 | String get description => ['隐藏', '红点', '数字'][index]; 5 | } 6 | 7 | extension DynamicBadgeModeCode on DynamicBadgeMode { 8 | int get code => [0, 1, 2][index]; 9 | } 10 | -------------------------------------------------------------------------------- /lib/models/common/dynamics_type.dart: -------------------------------------------------------------------------------- 1 | enum DynamicsType { 2 | all, 3 | video, 4 | pgc, 5 | article, 6 | } 7 | 8 | extension BusinessTypeExtension on DynamicsType { 9 | String get values => ['all', 'video', 'pgc', 'article'][index]; 10 | String get labels => ['全部', '投稿', '番剧', '专栏'][index]; 11 | } 12 | -------------------------------------------------------------------------------- /lib/models/common/gesture_mode.dart: -------------------------------------------------------------------------------- 1 | enum FullScreenGestureMode { 2 | /// 从上滑到下 3 | fromToptoBottom, 4 | 5 | /// 从下滑到上 6 | fromBottomtoTop, 7 | } 8 | 9 | extension FullScreenGestureModeExtension on FullScreenGestureMode { 10 | String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; 11 | String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; 12 | } 13 | -------------------------------------------------------------------------------- /lib/models/common/index.dart: -------------------------------------------------------------------------------- 1 | library commonn_model; 2 | 3 | export './business_type.dart'; 4 | export './gesture_mode.dart'; 5 | -------------------------------------------------------------------------------- /lib/models/common/nav_bar_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../pages/dynamics/index.dart'; 4 | import '../../pages/home/index.dart'; 5 | import '../../pages/media/index.dart'; 6 | import '../../pages/rank/index.dart'; 7 | 8 | List defaultNavigationBars = [ 9 | { 10 | 'id': 0, 11 | 'icon': const Icon( 12 | Icons.home_outlined, 13 | size: 21, 14 | ), 15 | 'selectIcon': const Icon( 16 | Icons.home, 17 | size: 21, 18 | ), 19 | 'label': "首页", 20 | 'count': 0, 21 | 'page': const HomePage(), 22 | }, 23 | { 24 | 'id': 1, 25 | 'icon': const Icon( 26 | Icons.trending_up, 27 | size: 21, 28 | ), 29 | 'selectIcon': const Icon( 30 | Icons.trending_up_outlined, 31 | size: 21, 32 | ), 33 | 'label': "排行榜", 34 | 'count': 0, 35 | 'page': const RankPage(), 36 | }, 37 | { 38 | 'id': 2, 39 | 'icon': const Icon( 40 | Icons.motion_photos_on_outlined, 41 | size: 21, 42 | ), 43 | 'selectIcon': const Icon( 44 | Icons.motion_photos_on, 45 | size: 21, 46 | ), 47 | 'label': "动态", 48 | 'count': 0, 49 | 'page': const DynamicsPage(), 50 | }, 51 | { 52 | 'id': 3, 53 | 'icon': const Icon( 54 | Icons.video_collection_outlined, 55 | size: 20, 56 | ), 57 | 'selectIcon': const Icon( 58 | Icons.video_collection, 59 | size: 21, 60 | ), 61 | 'label': "媒体库", 62 | 'count': 0, 63 | 'page': const MediaPage(), 64 | } 65 | ]; 66 | -------------------------------------------------------------------------------- /lib/models/common/rcmd_type.dart: -------------------------------------------------------------------------------- 1 | // 首页推荐类型 2 | enum RcmdType { web, app, notLogin } 3 | 4 | extension RcmdTypeExtension on RcmdType { 5 | String get values => ['web', 'app', 'notLogin'][index]; 6 | String get labels => ['web端', 'app端', '游客模式'][index]; 7 | } 8 | -------------------------------------------------------------------------------- /lib/models/common/reply_sort_type.dart: -------------------------------------------------------------------------------- 1 | enum ReplySortType { time, like } 2 | 3 | extension ReplySortTypeExtension on ReplySortType { 4 | String get titles => ['最新评论', '最热评论'][index]; 5 | String get labels => ['最新', '最热'][index]; 6 | } 7 | -------------------------------------------------------------------------------- /lib/models/common/reply_type.dart: -------------------------------------------------------------------------------- 1 | enum ReplyType { 2 | unset, 3 | // 视频 4 | video, 5 | // 话题 6 | topic, 7 | // 8 | unset2, 9 | // 活动 10 | activity, 11 | // 小视频 12 | videoS, 13 | // 小黑屋封禁信息 14 | blockMsg, 15 | // 公告信息 16 | publicMsg, 17 | // 直播活动 18 | liveActivity, 19 | // 活动稿件 20 | activityFile, 21 | // 直播公告 22 | livePublic, 23 | // 相簿 24 | album, 25 | // 专栏 26 | column, 27 | // 票务 28 | ticket, 29 | // 音频 30 | audio, 31 | // 风纪委员会 32 | unset3, 33 | // 点评 34 | comment, 35 | // 动态 36 | dynamics, 37 | // 播单 38 | playList, 39 | // 音乐播单 40 | musicPlayList, 41 | // 漫画 42 | comics1, 43 | // 漫画 44 | comics2, 45 | // 漫画 46 | comics3, 47 | // 课程 48 | course, 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/common/search_type.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | enum SearchType { 3 | // 视频:video 4 | video, 5 | // 番剧:media_bangumi, 6 | media_bangumi, 7 | // 影视:media_ft 8 | // media_ft, 9 | // 直播间及主播:live 10 | // live, 11 | // 直播间:live_room 12 | live_room, 13 | // 主播:live_user 14 | // live_user, 15 | // 话题:topic 16 | // topic, 17 | // 用户:bili_user 18 | bili_user, 19 | // 专栏:article 20 | article, 21 | // 相簿:photo 22 | // photo 23 | } 24 | 25 | extension SearchTypeExtension on SearchType { 26 | String get type => 27 | ['video', 'media_bangumi', 'live_room', 'bili_user', 'article'][index]; 28 | String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index]; 29 | } 30 | 31 | // 搜索类型为视频时 32 | enum ArchiveFilterType { 33 | totalrank, 34 | click, 35 | pubdate, 36 | dm, 37 | stow, 38 | scores, 39 | // 专栏 40 | // attention, 41 | } 42 | 43 | extension ArchiveFilterTypeExtension on ArchiveFilterType { 44 | String get description => 45 | ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index]; 46 | } 47 | 48 | // 搜索类型为专栏时 49 | enum ArticleFilterType { 50 | // 综合排序 51 | totalrank, 52 | // 最新发布 53 | pubdate, 54 | // 最多点击 55 | click, 56 | // 最多喜欢 57 | attention, 58 | // 最多评论 59 | scores, 60 | } 61 | 62 | extension ArticleFilterTypeExtension on ArticleFilterType { 63 | String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index]; 64 | } 65 | -------------------------------------------------------------------------------- /lib/models/common/tab_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/pages/bangumi/index.dart'; 4 | import 'package:pilipala/pages/hot/index.dart'; 5 | import 'package:pilipala/pages/live/index.dart'; 6 | import 'package:pilipala/pages/rcmd/index.dart'; 7 | 8 | enum TabType { live, rcmd, hot, bangumi } 9 | 10 | extension TabTypeDesc on TabType { 11 | String get description => ['直播', '推荐', '热门', '番剧'][index]; 12 | String get id => ['live', 'rcmd', 'hot', 'bangumi'][index]; 13 | } 14 | 15 | List tabsConfig = [ 16 | { 17 | 'icon': const Icon( 18 | Icons.live_tv_outlined, 19 | size: 15, 20 | ), 21 | 'label': '直播', 22 | 'type': TabType.live, 23 | 'ctr': Get.find, 24 | 'page': const LivePage(), 25 | }, 26 | { 27 | 'icon': const Icon( 28 | Icons.thumb_up_off_alt_outlined, 29 | size: 15, 30 | ), 31 | 'label': '推荐', 32 | 'type': TabType.rcmd, 33 | 'ctr': Get.find, 34 | 'page': const RcmdPage(), 35 | }, 36 | { 37 | 'icon': const Icon( 38 | Icons.whatshot_outlined, 39 | size: 15, 40 | ), 41 | 'label': '热门', 42 | 'type': TabType.hot, 43 | 'ctr': Get.find, 44 | 'page': const HotPage(), 45 | }, 46 | { 47 | 'icon': const Icon( 48 | Icons.play_circle_outlined, 49 | size: 15, 50 | ), 51 | 'label': '番剧', 52 | 'type': TabType.bangumi, 53 | 'ctr': Get.find, 54 | 'page': const BangumiPage(), 55 | }, 56 | ]; 57 | -------------------------------------------------------------------------------- /lib/models/common/theme_type.dart: -------------------------------------------------------------------------------- 1 | enum ThemeType { 2 | light, 3 | dark, 4 | system, 5 | } 6 | 7 | extension ThemeTypeDesc on ThemeType { 8 | String get description => ['浅色', '深色', '跟随系统'][index]; 9 | } 10 | 11 | extension ThemeTypeCode on ThemeType { 12 | int get code => [0, 1, 2][index]; 13 | } 14 | -------------------------------------------------------------------------------- /lib/models/common/video_episode_type.dart: -------------------------------------------------------------------------------- 1 | enum VideoEpidoesType { 2 | videoEpisode, 3 | videoPart, 4 | bangumiEpisode, 5 | } 6 | -------------------------------------------------------------------------------- /lib/models/fans/result.dart: -------------------------------------------------------------------------------- 1 | class FansDataModel { 2 | FansDataModel({ 3 | this.total, 4 | this.list, 5 | }); 6 | 7 | int? total; 8 | List? list; 9 | 10 | FansDataModel.fromJson(Map json) { 11 | total = json['total']; 12 | list = json['list'] 13 | .map((e) => FansItemModel.fromJson(e)) 14 | .toList(); 15 | } 16 | } 17 | 18 | class FansItemModel { 19 | FansItemModel({ 20 | this.mid, 21 | this.attribute, 22 | this.mtime, 23 | this.tag, 24 | this.special, 25 | this.uname, 26 | this.face, 27 | this.sign, 28 | this.officialVerify, 29 | }); 30 | 31 | int? mid; 32 | int? attribute; 33 | int? mtime; 34 | List? tag; 35 | int? special; 36 | String? uname; 37 | String? face; 38 | String? sign; 39 | Map? officialVerify; 40 | 41 | FansItemModel.fromJson(Map json) { 42 | mid = json['mid']; 43 | attribute = json['attribute']; 44 | mtime = json['mtime']; 45 | tag = json['tag']; 46 | special = json['special']; 47 | uname = json['uname']; 48 | face = json['face']; 49 | sign = json['sign'] == '' ? '还没有签名' : json['sign']; 50 | officialVerify = json['official_verify']; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/models/follow/result.dart: -------------------------------------------------------------------------------- 1 | class FollowDataModel { 2 | FollowDataModel({ 3 | this.total, 4 | this.list, 5 | }); 6 | 7 | int? total; 8 | List? list; 9 | 10 | FollowDataModel.fromJson(Map json) { 11 | total = json['total'] ?? 0; 12 | list = json['list'] 13 | .map((e) => FollowItemModel.fromJson(e)) 14 | .toList(); 15 | } 16 | } 17 | 18 | class FollowItemModel { 19 | FollowItemModel({ 20 | this.mid, 21 | this.attribute, 22 | // this.mtime, 23 | this.tag, 24 | this.special, 25 | this.uname, 26 | this.face, 27 | this.sign, 28 | this.officialVerify, 29 | }); 30 | 31 | int? mid; 32 | int? attribute; 33 | // int? mtime; 34 | List? tag; 35 | int? special; 36 | String? uname; 37 | String? face; 38 | String? sign; 39 | Map? officialVerify; 40 | 41 | FollowItemModel.fromJson(Map json) { 42 | mid = json['mid']; 43 | attribute = json['attribute']; 44 | // mtime = json['mtime']; 45 | tag = json['tag']; 46 | special = json['special']; 47 | uname = json['uname']; 48 | face = json['face']; 49 | sign = json['sign'] == '' ? '还没有签名' : json['sign']; 50 | officialVerify = json['official_verify']; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/models/github/latest.dart: -------------------------------------------------------------------------------- 1 | class LatestDataModel { 2 | LatestDataModel({ 3 | this.url, 4 | this.tagName, 5 | this.createdAt, 6 | this.assets, 7 | this.body, 8 | }); 9 | 10 | String? url; 11 | String? tagName; 12 | String? createdAt; 13 | List? assets; 14 | String? body; 15 | 16 | LatestDataModel.fromJson(Map json) { 17 | url = json['url']; 18 | tagName = json['tag_name']; 19 | createdAt = json['created_at']; 20 | assets = json['assets'] != null 21 | ? json['assets'].map((e) => AssetItem.fromJson(e)).toList() 22 | : []; 23 | body = json['body']; 24 | } 25 | } 26 | 27 | class AssetItem { 28 | AssetItem({ 29 | this.url, 30 | this.name, 31 | this.size, 32 | this.downloadCount, 33 | this.downloadUrl, 34 | }); 35 | 36 | String? url; 37 | String? name; 38 | int? size; 39 | int? downloadCount; 40 | String? downloadUrl; 41 | 42 | AssetItem.fromJson(Map json) { 43 | url = json['url']; 44 | name = json['name']; 45 | size = json['size']; 46 | downloadCount = json['download_count']; 47 | downloadUrl = json['browser_download_url']; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/live/quality.dart: -------------------------------------------------------------------------------- 1 | enum LiveQuality { 2 | dolby, 3 | super4K, 4 | origin, 5 | bluRay, 6 | superHD, 7 | smooth, 8 | flunt, 9 | } 10 | 11 | extension LiveQualityCode on LiveQuality { 12 | static final List _codeList = [ 13 | 30000, 14 | 20000, 15 | 10000, 16 | 400, 17 | 250, 18 | 150, 19 | 80, 20 | ]; 21 | int get code => _codeList[index]; 22 | 23 | static LiveQuality? fromCode(int code) { 24 | final index = _codeList.indexOf(code); 25 | if (index != -1) { 26 | return LiveQuality.values[index]; 27 | } 28 | return null; 29 | } 30 | } 31 | 32 | extension VideoQualityDesc on LiveQuality { 33 | static final List _descList = [ 34 | '杜比', 35 | '4K', 36 | '原画', 37 | '蓝光', 38 | '超清', 39 | '高清', 40 | '流畅', 41 | ]; 42 | get description => _descList[index]; 43 | } 44 | -------------------------------------------------------------------------------- /lib/models/login/index.dart: -------------------------------------------------------------------------------- 1 | class CaptchaDataModel { 2 | CaptchaDataModel({ 3 | this.type, 4 | this.token, 5 | this.geetest, 6 | this.tencent, 7 | this.validate, 8 | this.seccode, 9 | }); 10 | 11 | String? type; 12 | String? token; 13 | GeetestData? geetest; 14 | Tencent? tencent; 15 | String? validate; 16 | String? seccode; 17 | 18 | CaptchaDataModel.fromJson(Map json) { 19 | type = json["type"]; 20 | token = json["token"]; 21 | geetest = 22 | json["geetest"] != null ? GeetestData.fromJson(json["geetest"]) : null; 23 | tencent = 24 | json["tencent"] != null ? Tencent.fromJson(json["tencent"]) : null; 25 | } 26 | } 27 | 28 | class GeetestData { 29 | GeetestData({ 30 | this.challenge, 31 | this.gt, 32 | }); 33 | 34 | String? challenge; 35 | String? gt; 36 | 37 | GeetestData.fromJson(Map json) { 38 | challenge = json["challenge"]; 39 | gt = json["gt"]; 40 | } 41 | } 42 | 43 | class Tencent { 44 | Tencent({this.appid}); 45 | String? appid; 46 | Tencent.fromJson(Map json) { 47 | appid = json["appid"]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/models/member/article.dart: -------------------------------------------------------------------------------- 1 | class MemberArticleDataModel { 2 | MemberArticleDataModel({ 3 | this.hasMore, 4 | this.items, 5 | this.offset, 6 | this.updateNum, 7 | }); 8 | 9 | bool? hasMore; 10 | List? items; 11 | String? offset; 12 | int? updateNum; 13 | 14 | MemberArticleDataModel.fromJson(Map json) { 15 | hasMore = json['has_more']; 16 | items = json['items'] 17 | .map((e) => MemberArticleItemModel.fromJson(e)) 18 | .toList(); 19 | offset = json['offset']; 20 | updateNum = json['update_num']; 21 | } 22 | } 23 | 24 | class MemberArticleItemModel { 25 | MemberArticleItemModel({ 26 | this.content, 27 | this.cover, 28 | this.jumpUrl, 29 | this.opusId, 30 | this.stat, 31 | }); 32 | 33 | String? content; 34 | Map? cover; 35 | String? jumpUrl; 36 | String? opusId; 37 | Map? stat; 38 | 39 | MemberArticleItemModel.fromJson(Map json) { 40 | content = json['content']; 41 | cover = json['cover']; 42 | jumpUrl = json['jump_url']; 43 | opusId = json['opus_id']; 44 | stat = json['stat']; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/models/member/tags.dart: -------------------------------------------------------------------------------- 1 | class MemberTagItemModel { 2 | MemberTagItemModel({ 3 | this.count, 4 | this.name, 5 | this.tagid, 6 | this.tip, 7 | this.checked, 8 | }); 9 | 10 | int? count; 11 | String? name; 12 | int? tagid; 13 | String? tip; 14 | bool? checked; 15 | 16 | MemberTagItemModel.fromJson(Map json) { 17 | count = json['count']; 18 | name = json['name']; 19 | tagid = json['tagid']; 20 | tip = json['tip']; 21 | checked = false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/model_owner.dart: -------------------------------------------------------------------------------- 1 | class Owner { 2 | Owner({ 3 | this.mid, 4 | this.name, 5 | this.face, 6 | }); 7 | 8 | int? mid; 9 | String? name; 10 | String? face; 11 | 12 | Owner.fromJson(Map json) { 13 | mid = json["mid"]; 14 | name = json["name"]; 15 | face = json['face']; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/model_rec_video_item.dart: -------------------------------------------------------------------------------- 1 | import './model_owner.dart'; 2 | 3 | class RecVideoItemModel { 4 | RecVideoItemModel({ 5 | this.id, 6 | this.bvid, 7 | this.cid, 8 | this.goto, 9 | this.uri, 10 | this.pic, 11 | this.title, 12 | this.duration, 13 | this.pubdate, 14 | this.owner, 15 | this.stat, 16 | this.isFollowed, 17 | this.rcmdReason, 18 | }); 19 | 20 | int? id = -1; 21 | String? bvid = ''; 22 | int? cid = -1; 23 | String? goto = ''; 24 | String? uri = ''; 25 | String? pic = ''; 26 | String? title = ''; 27 | int? duration = -1; 28 | int? pubdate = -1; 29 | Owner? owner; 30 | Stat? stat; 31 | int? isFollowed; 32 | String? rcmdReason; 33 | 34 | RecVideoItemModel.fromJson(Map json) { 35 | id = json["id"]; 36 | bvid = json["bvid"]; 37 | cid = json["cid"]; 38 | goto = json["goto"]; 39 | uri = json["uri"]; 40 | pic = json["pic"]; 41 | title = json["title"]; 42 | duration = json["duration"]; 43 | pubdate = json["pubdate"]; 44 | owner = Owner.fromJson(json["owner"]); 45 | stat = Stat.fromJson(json["stat"]); 46 | isFollowed = json["is_followed"] ?? 0; 47 | rcmdReason = json["rcmd_reason"]?['content']; 48 | } 49 | } 50 | 51 | class Stat { 52 | Stat({ 53 | this.view, 54 | this.like, 55 | this.danmu, 56 | }); 57 | 58 | int? view; 59 | int? like; 60 | int? danmu; 61 | Stat.fromJson(Map json) { 62 | // 无需在model中转换以保留原始数据,在view层处理即可 63 | view = json["view"]; 64 | like = json["like"]; 65 | danmu = json['danmaku']; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/models/search/all.dart: -------------------------------------------------------------------------------- 1 | class SearchAllModel { 2 | SearchAllModel({this.topTList}); 3 | 4 | Map? topTList; 5 | 6 | SearchAllModel.fromJson(Map json) { 7 | topTList = json['top_tlist']; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/models/search/hot.dart: -------------------------------------------------------------------------------- 1 | class HotSearchModel { 2 | HotSearchModel({ 3 | this.list, 4 | }); 5 | 6 | List? list; 7 | 8 | HotSearchModel.fromJson(Map json) { 9 | list = json['list'] 10 | .map((e) => HotSearchItem.fromJson(e)) 11 | .toList(); 12 | } 13 | } 14 | 15 | class HotSearchItem { 16 | HotSearchItem({ 17 | this.keyword, 18 | this.showName, 19 | this.wordType, 20 | this.icon, 21 | }); 22 | 23 | String? keyword; 24 | String? showName; 25 | // 4/5热 11话题 8普通 7直播 26 | int? wordType; 27 | String? icon; 28 | 29 | HotSearchItem.fromJson(Map json) { 30 | keyword = json['keyword']; 31 | showName = json['show_name']; 32 | wordType = json['word_type']; 33 | icon = json['icon']; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/models/user/black.dart: -------------------------------------------------------------------------------- 1 | class BlackListDataModel { 2 | BlackListDataModel({ 3 | this.list, 4 | this.total, 5 | }); 6 | 7 | List? list; 8 | int? total; 9 | 10 | BlackListDataModel.fromJson(Map json) { 11 | list = json['list'] 12 | .map((e) => BlackListItem.fromJson(e)) 13 | .toList(); 14 | total = json['total']; 15 | } 16 | } 17 | 18 | class BlackListItem { 19 | BlackListItem({ 20 | this.face, 21 | this.mid, 22 | this.mtime, 23 | this.uname, 24 | }); 25 | 26 | String? face; 27 | int? mid; 28 | int? mtime; 29 | String? uname; 30 | 31 | BlackListItem.fromJson(Map json) { 32 | face = json['face']; 33 | mid = json['mid']; 34 | mtime = json['mtime']; 35 | uname = json['uname']; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/models/user/stat.dart: -------------------------------------------------------------------------------- 1 | class UserStat { 2 | UserStat({ 3 | this.following, 4 | this.follower, 5 | this.dynamicCount, 6 | }); 7 | 8 | int? following; 9 | int? follower; 10 | int? dynamicCount; 11 | 12 | UserStat.fromJson(Map json) { 13 | following = json['following']; 14 | follower = json['follower']; 15 | dynamicCount = json['dynamic_count']; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/video/play/ao_output.dart: -------------------------------------------------------------------------------- 1 | final List aoOutputList = [ 2 | {'title': 'audiotrack,opensles', 'value': '0'}, 3 | {'title': 'opensles,audiotrack', 'value': '1'}, 4 | {'title': 'audiotrack', 'value': '2'}, 5 | {'title': 'opensles', 'value': '3'}, 6 | ]; 7 | -------------------------------------------------------------------------------- /lib/models/video/reply/config.dart: -------------------------------------------------------------------------------- 1 | class ReplyConfig { 2 | ReplyConfig({ 3 | this.showtopic, 4 | this.showUpFlag, 5 | this.readOnly, 6 | }); 7 | 8 | int? showtopic; 9 | bool? showUpFlag; 10 | bool? readOnly; 11 | 12 | ReplyConfig.fromJson(Map json) { 13 | showtopic = json['showtopic']; 14 | showUpFlag = json['show_up_flag']; 15 | readOnly = json['read_only']; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/models/video/reply/content.dart: -------------------------------------------------------------------------------- 1 | class ReplyContent { 2 | ReplyContent({ 3 | this.message, 4 | this.atNameToMid, // @的用户的mid null 5 | this.members, // 被@的用户List 如果有的话 [] 6 | this.emote, // 表情包 如果有的话 null 7 | this.jumpUrl, // {} 8 | this.pictures, // {} 9 | this.vote, 10 | this.richText, 11 | this.isText, 12 | this.topicsMeta, 13 | }); 14 | 15 | String? message; 16 | Map? atNameToMid; 17 | List? members; 18 | Map? emote; 19 | Map? jumpUrl; 20 | List? pictures; 21 | Map? vote; 22 | Map? richText; 23 | bool? isText; 24 | Map? topicsMeta; 25 | 26 | ReplyContent.fromJson(Map json) { 27 | message = json['message'] 28 | .replaceAll('>', '>') 29 | .replaceAll('"', '"') 30 | .replaceAll(''', "'"); 31 | atNameToMid = json['at_name_to_mid'] ?? {}; 32 | members = json['members'] != null 33 | ? json['members'] 34 | .map((e) => MemberItemModel.fromJson(e)) 35 | .toList() 36 | : []; 37 | emote = json['emote'] ?? {}; 38 | jumpUrl = json['jump_url'] ?? {}; 39 | pictures = json['pictures'] ?? []; 40 | vote = json['vote'] ?? {}; 41 | richText = json['rich_text'] ?? {}; 42 | // 不包含@ 笔记 图片的时候,文字可折叠 43 | isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; 44 | topicsMeta = json['topics_meta'] ?? {}; 45 | } 46 | } 47 | 48 | class MemberItemModel { 49 | MemberItemModel({ 50 | required this.mid, 51 | required this.uname, 52 | }); 53 | 54 | late String mid; 55 | late String uname; 56 | 57 | MemberItemModel.fromJson(Map json) { 58 | mid = json['mid']; 59 | uname = json['uname']; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/models/video/reply/data.dart: -------------------------------------------------------------------------------- 1 | import 'package:pilipala/models/video/reply/item.dart'; 2 | 3 | import 'config.dart'; 4 | import 'page.dart'; 5 | import 'upper.dart'; 6 | 7 | class ReplyData { 8 | ReplyData({ 9 | this.page, 10 | this.config, 11 | this.replies, 12 | this.topReplies, 13 | this.upper, 14 | }); 15 | 16 | ReplyPage? page; 17 | ReplyConfig? config; 18 | late List? replies; 19 | late List? topReplies; 20 | ReplyUpper? upper; 21 | 22 | ReplyData.fromJson(Map json) { 23 | page = ReplyPage.fromJson(json['page']); 24 | config = ReplyConfig.fromJson(json['config']); 25 | replies = json['replies'] != null 26 | ? json['replies'] 27 | .map( 28 | (item) => ReplyItemModel.fromJson(item, json['upper']['mid'])) 29 | .toList() 30 | : []; 31 | topReplies = json['top_replies'] != null 32 | ? json['top_replies'] 33 | .map((item) => ReplyItemModel.fromJson( 34 | item, json['upper']['mid'], 35 | isTopStatus: true)) 36 | .toList() 37 | : []; 38 | upper = ReplyUpper.fromJson(json['upper']); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/models/video/reply/member.dart: -------------------------------------------------------------------------------- 1 | class ReplyMember { 2 | ReplyMember({ 3 | this.mid, 4 | this.uname, 5 | this.sign, 6 | this.avatar, 7 | this.level, 8 | this.pendant, 9 | this.officialVerify, 10 | this.vip, 11 | this.fansDetail, 12 | }); 13 | 14 | String? mid; 15 | String? uname; 16 | String? sign; 17 | String? avatar; 18 | int? level; 19 | Pendant? pendant; 20 | Map? officialVerify; 21 | Map? vip; 22 | Map? fansDetail; 23 | UserSailing? userSailing; 24 | 25 | ReplyMember.fromJson(Map json) { 26 | mid = json['mid']; 27 | uname = json['uname']; 28 | sign = json['sign']; 29 | avatar = json['avatar']; 30 | level = json['level_info']['current_level']; 31 | pendant = Pendant.fromJson(json['pendant']); 32 | officialVerify = json['officia_verify']; 33 | vip = json['vip']; 34 | fansDetail = json['fans_detail']; 35 | userSailing = json['user_sailing'] != null 36 | ? UserSailing.fromJson(json['user_sailing']) 37 | : UserSailing(); 38 | } 39 | } 40 | 41 | class Pendant { 42 | Pendant({ 43 | this.pid, 44 | this.name, 45 | this.image, 46 | }); 47 | 48 | int? pid; 49 | String? name; 50 | String? image; 51 | 52 | Pendant.fromJson(Map json) { 53 | pid = json['pid']; 54 | name = json['name']; 55 | image = json['image']; 56 | } 57 | } 58 | 59 | class UserSailing { 60 | UserSailing({this.pendant, this.cardbg}); 61 | 62 | Map? pendant; 63 | Map? cardbg; 64 | 65 | UserSailing.fromJson(Map json) { 66 | pendant = json['pendant']; 67 | cardbg = json['cardbg']; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/models/video/reply/page.dart: -------------------------------------------------------------------------------- 1 | class ReplyPage { 2 | ReplyPage({ 3 | this.num, 4 | this.size, 5 | this.count, 6 | this.acount, 7 | }); 8 | 9 | int? num; 10 | int? size; 11 | int? count; 12 | int? acount; 13 | 14 | ReplyPage.fromJson(Map json) { 15 | num = json['num']; 16 | size = json['size']; 17 | count = json['count']; 18 | acount = json['acount']; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/models/video/reply/top_replies.dart: -------------------------------------------------------------------------------- 1 | class ReplyTop {} 2 | -------------------------------------------------------------------------------- /lib/models/video/reply/upper.dart: -------------------------------------------------------------------------------- 1 | import 'item.dart'; 2 | 3 | class ReplyUpper { 4 | ReplyUpper({ 5 | this.mid, 6 | this.top, 7 | }); 8 | 9 | int? mid; 10 | ReplyItemModel? top; 11 | 12 | ReplyUpper.fromJson(Map json) { 13 | mid = json['mid']; 14 | top = json['top'] != null 15 | ? ReplyItemModel.fromJson(json['top'], json['mid']) 16 | : null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/models/video/subTitile/content.dart: -------------------------------------------------------------------------------- 1 | class SubTitileContentModel { 2 | double? from; 3 | double? to; 4 | int? location; 5 | String? content; 6 | 7 | SubTitileContentModel({ 8 | this.from, 9 | this.to, 10 | this.location, 11 | this.content, 12 | }); 13 | 14 | SubTitileContentModel.fromJson(Map json) { 15 | from = json['from']; 16 | to = json['to']; 17 | location = json['location']; 18 | content = json['content']; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/pages/bangumi/index.dart: -------------------------------------------------------------------------------- 1 | library bangumi_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/bangumi/introduction/index.dart: -------------------------------------------------------------------------------- 1 | library bangumi_intro; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/contact/contact_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ContactPage extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Center(child: Text('联系人页面')); 7 | } 8 | } -------------------------------------------------------------------------------- /lib/pages/danmaku/index.dart: -------------------------------------------------------------------------------- 1 | library pldanmaku; 2 | 3 | export './controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamics/detail/index.dart: -------------------------------------------------------------------------------- 1 | library dynamic_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamics/index.dart: -------------------------------------------------------------------------------- 1 | library dynamics; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/dynamics/up_dynamic/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/http/dynamics.dart'; 4 | import 'package:pilipala/models/dynamics/result.dart'; 5 | import 'package:pilipala/models/dynamics/up.dart'; 6 | 7 | class UpDynamicsController extends GetxController { 8 | UpDynamicsController(this.upInfo); 9 | UpItem upInfo; 10 | RxList dynamicsList = [].obs; 11 | RxBool isLoadingDynamic = false.obs; 12 | String? offset = ''; 13 | int page = 1; 14 | 15 | Future queryFollowDynamic({type = 'init'}) async { 16 | if (type == 'init') { 17 | dynamicsList.clear(); 18 | } 19 | // 下拉刷新数据渲染时会触发onLoad 20 | if (type == 'onLoad' && page == 1) { 21 | return; 22 | } 23 | isLoadingDynamic.value = true; 24 | var res = await DynamicsHttp.followDynamic( 25 | page: type == 'init' ? 1 : page, 26 | type: 'all', 27 | offset: offset, 28 | mid: upInfo.mid, 29 | ); 30 | isLoadingDynamic.value = false; 31 | if (res['status']) { 32 | if (type == 'onLoad' && res['data'].items.isEmpty) { 33 | SmartDialog.showToast('没有更多了'); 34 | return; 35 | } 36 | if (type == 'init') { 37 | dynamicsList.value = res['data'].items; 38 | } else { 39 | dynamicsList.addAll(res['data'].items); 40 | } 41 | offset = res['data'].offset; 42 | page++; 43 | } 44 | return res; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/pages/dynamics/up_dynamic/index.dart: -------------------------------------------------------------------------------- 1 | library up_dynamics; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/emote/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | import '../../http/reply.dart'; 5 | import '../../models/video/reply/emote.dart'; 6 | 7 | class EmotePanelController extends GetxController 8 | with GetTickerProviderStateMixin { 9 | late List emotePackage; 10 | late TabController tabController; 11 | 12 | Future getEmote() async { 13 | var res = await ReplyHttp.getEmoteList(business: 'reply'); 14 | if (res['status']) { 15 | emotePackage = res['data'].packages; 16 | tabController = TabController(length: emotePackage.length, vsync: this); 17 | } 18 | return res; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/pages/emote/index.dart: -------------------------------------------------------------------------------- 1 | library emote; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fan/index.dart: -------------------------------------------------------------------------------- 1 | library fan; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fan/widgets/fan_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/common/widgets/network_img_layer.dart'; 4 | import 'package:pilipala/utils/utils.dart'; 5 | import 'package:pilipala/models/fans/result.dart'; 6 | 7 | Widget fanItem({required FansItemModel item}) { 8 | String heroTag = Utils.makeHeroTag(item.mid); 9 | return Builder( 10 | builder: (context) => ListTile( 11 | onTap: () => Get.toNamed('/member?mid=${item.mid}', 12 | arguments: {'face': item.face, 'heroTag': heroTag}), 13 | leading: Hero( 14 | tag: heroTag, 15 | child: NetworkImgLayer( 16 | width: 38, 17 | height: 38, 18 | type: 'avatar', 19 | src: item.face ?? '', 20 | ), 21 | ), 22 | title: Text( 23 | item.uname ?? '', 24 | style: TextStyle( 25 | color: Theme.of(context).colorScheme.onSurface, 26 | ), 27 | ), 28 | subtitle: Text( 29 | item.sign ?? '', 30 | maxLines: 1, 31 | overflow: TextOverflow.ellipsis, 32 | style: TextStyle( 33 | color: Theme.of(context).colorScheme.outline, 34 | ), 35 | ), 36 | dense: true, 37 | trailing: const SizedBox(width: 6), 38 | ), 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /lib/pages/fan/widgets/no_login_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/utils/utils.dart'; 4 | 5 | class NoLoginPanel extends StatelessWidget { 6 | const NoLoginPanel({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return SliverToBoxAdapter( 11 | child: Container( 12 | height: 400, 13 | padding: const EdgeInsets.all(16), 14 | child: Column( 15 | mainAxisAlignment: MainAxisAlignment.center, 16 | children: [ 17 | Icon( 18 | Icons.person_outline, 19 | size: 80, 20 | color: Theme.of(context).colorScheme.outline, 21 | ), 22 | const SizedBox(height: 16), 23 | Text( 24 | '请先登录', 25 | style: Theme.of(context).textTheme.titleMedium!.copyWith( 26 | color: Theme.of(context).colorScheme.outline, 27 | ), 28 | ), 29 | const SizedBox(height: 24), 30 | FilledButton( 31 | onPressed: () => Get.toNamed('/loginPage'), 32 | child: const Text('立即登录'), 33 | ), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /lib/pages/fav/index.dart: -------------------------------------------------------------------------------- 1 | library fav; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fav_detail/index.dart: -------------------------------------------------------------------------------- 1 | library favdetail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fav_edit/index.dart: -------------------------------------------------------------------------------- 1 | library fav_edit; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/fav_search/index.dart: -------------------------------------------------------------------------------- 1 | library fav_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/follow/index.dart: -------------------------------------------------------------------------------- 1 | library following; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/follow_search/index.dart: -------------------------------------------------------------------------------- 1 | library follow_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/history/index.dart: -------------------------------------------------------------------------------- 1 | library history; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/history_search/index.dart: -------------------------------------------------------------------------------- 1 | library history_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/home/index.dart: -------------------------------------------------------------------------------- 1 | library home; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/hot/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:pilipala/http/video.dart'; 4 | import 'package:pilipala/models/model_hot_video_item.dart'; 5 | 6 | class HotController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | final int _count = 20; 9 | int _currentPage = 1; 10 | RxList videoList = [].obs; 11 | bool isLoadingMore = false; 12 | bool flag = false; 13 | OverlayEntry? popupDialog; 14 | 15 | // 获取推荐 16 | Future queryHotFeed(type) async { 17 | var res = await VideoHttp.hotVideoList( 18 | pn: _currentPage, 19 | ps: _count, 20 | ); 21 | if (res['status']) { 22 | if (type == 'init') { 23 | videoList.value = res['data']; 24 | } else if (type == 'onRefresh') { 25 | videoList.insertAll(0, res['data']); 26 | } else if (type == 'onLoad') { 27 | videoList.addAll(res['data']); 28 | } 29 | _currentPage += 1; 30 | } 31 | isLoadingMore = false; 32 | return res; 33 | } 34 | 35 | // 下拉刷新 36 | Future onRefresh() async { 37 | queryHotFeed('onRefresh'); 38 | } 39 | 40 | // 上拉加载 41 | Future onLoad() async { 42 | queryHotFeed('onLoad'); 43 | } 44 | 45 | // 返回顶部并刷新 46 | void animateToTop() async { 47 | if (scrollController.offset >= 48 | MediaQuery.of(Get.context!).size.height * 5) { 49 | scrollController.jumpTo(0); 50 | } else { 51 | await scrollController.animateTo(0, 52 | duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/pages/hot/index.dart: -------------------------------------------------------------------------------- 1 | library hot; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/html/index.dart: -------------------------------------------------------------------------------- 1 | library html_render; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/later/index.dart: -------------------------------------------------------------------------------- 1 | library later; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/live/index.dart: -------------------------------------------------------------------------------- 1 | library live; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/live_room/index.dart: -------------------------------------------------------------------------------- 1 | library liveroom; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/login/index.dart: -------------------------------------------------------------------------------- 1 | library login; 2 | 3 | export './controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/main/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import '../../models/common/dynamic_badge_mode.dart'; 5 | import '../../http/common.dart'; 6 | import '../message/message_page.dart'; 7 | import '../fan/wrapper.dart'; 8 | import '../video/video_page.dart'; 9 | import '../ai/ai_page.dart'; 10 | import '../dynamics/index.dart'; 11 | 12 | class MainController extends GetxController { 13 | RxInt selectedIndex = 0.obs; 14 | final PageController pageController = PageController(); 15 | 16 | final RxBool userLogin = false.obs; 17 | final Rx dynamicBadgeType = DynamicBadgeMode.number.obs; 18 | final StreamController bottomBarStream = StreamController.broadcast(); 19 | bool imgPreviewStatus = false; 20 | 21 | final List pages = [ 22 | MessagePage(), 23 | FansPageWrapper(), 24 | VideoPageWrapper(), 25 | AiPage(), 26 | DynamicsPage(), 27 | ]; 28 | 29 | Future getUnreadDynamic() async { 30 | if (!userLogin.value) return; 31 | var res = await CommonHttp.unReadDynamic(); 32 | if (res['status']) { 33 | var data = res['data']; 34 | // TODO: 根据需要处理数据 35 | } 36 | } 37 | 38 | Color getBottomNavBarColor() { 39 | return selectedIndex.value == 2 ? Colors.black : const Color(0xFFEEEDF1); 40 | } 41 | 42 | Color getBottomNavItemColor(int index) { 43 | if (selectedIndex.value == 2) { 44 | return index == selectedIndex.value ? Colors.white : Colors.white.withOpacity(0.6); 45 | } else { 46 | return index == selectedIndex.value ? Colors.blue : Colors.grey; 47 | } 48 | } 49 | 50 | @override 51 | void onClose() { 52 | pageController.dispose(); 53 | bottomBarStream.close(); 54 | super.onClose(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/pages/main/index.dart: -------------------------------------------------------------------------------- 1 | library main; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/media/index.dart: -------------------------------------------------------------------------------- 1 | library media; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member/index.dart: -------------------------------------------------------------------------------- 1 | library member; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member/widgets/conis.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pilipala/common/constants.dart'; 3 | import 'package:pilipala/models/member/coin.dart'; 4 | import 'package:pilipala/pages/member_coin/widgets/item.dart'; 5 | 6 | class MemberCoinsPanel extends StatelessWidget { 7 | final List data; 8 | const MemberCoinsPanel({super.key, required this.data}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return LayoutBuilder( 13 | builder: (context, boxConstraints) { 14 | return GridView.builder( 15 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 16 | crossAxisCount: 2, // Use a fixed count for GridView 17 | crossAxisSpacing: StyleString.safeSpace, 18 | mainAxisSpacing: StyleString.safeSpace, 19 | childAspectRatio: 0.94, 20 | ), 21 | physics: const NeverScrollableScrollPhysics(), 22 | shrinkWrap: true, 23 | itemCount: data.length, 24 | itemBuilder: (context, i) { 25 | return MemberCoinsItem(coinItem: data[i]); 26 | }, 27 | ); 28 | }, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/pages/member/widgets/like.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:pilipala/common/constants.dart'; 3 | import 'package:pilipala/models/member/like.dart'; 4 | import 'package:pilipala/pages/member_like/widgets/item.dart'; 5 | 6 | class MemberLikePanel extends StatelessWidget { 7 | final List data; 8 | const MemberLikePanel({super.key, required this.data}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return LayoutBuilder( 13 | builder: (context, boxConstraints) { 14 | return GridView.builder( 15 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 16 | crossAxisCount: 2, // Use a fixed count for GridView 17 | crossAxisSpacing: StyleString.safeSpace, 18 | mainAxisSpacing: StyleString.safeSpace, 19 | childAspectRatio: 0.94, 20 | ), 21 | physics: const NeverScrollableScrollPhysics(), 22 | shrinkWrap: true, 23 | itemCount: data.length, 24 | itemBuilder: (context, i) { 25 | return MemberLikeItem(likeItem: data[i]); 26 | }, 27 | ); 28 | }, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/pages/member_archive/index.dart: -------------------------------------------------------------------------------- 1 | library member_archive; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_article/index.dart: -------------------------------------------------------------------------------- 1 | library member_article; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_coin/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MemberCoinController extends GetxController {} 4 | -------------------------------------------------------------------------------- /lib/pages/member_coin/index.dart: -------------------------------------------------------------------------------- 1 | library member_coin; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_coin/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MemberCoinPage extends StatefulWidget { 4 | const MemberCoinPage({super.key}); 5 | 6 | @override 7 | State createState() => _MemberCoinPageState(); 8 | } 9 | 10 | class _MemberCoinPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/member_dynamics/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/http/member.dart'; 4 | import 'package:pilipala/models/dynamics/result.dart'; 5 | 6 | class MemberDynamicsController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | late int mid; 9 | String offset = ''; 10 | int count = 0; 11 | bool hasMore = true; 12 | RxList dynamicsList = [].obs; 13 | 14 | @override 15 | void onInit() { 16 | super.onInit(); 17 | mid = int.parse(Get.parameters['mid']!); 18 | } 19 | 20 | Future getMemberDynamic(type) async { 21 | if (type == 'onRefresh') { 22 | offset = ''; 23 | dynamicsList.clear(); 24 | } 25 | if (offset == '-1') { 26 | return; 27 | } 28 | var res = await MemberHttp.memberDynamic( 29 | offset: offset, 30 | mid: mid, 31 | ); 32 | if (res['status']) { 33 | dynamicsList.addAll(res['data'].items); 34 | offset = res['data'].offset != '' ? res['data'].offset : '-1'; 35 | hasMore = res['data'].hasMore; 36 | } 37 | return res; 38 | } 39 | 40 | // 上拉加载 41 | Future onLoad() async { 42 | getMemberDynamic('onLoad'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/pages/member_dynamics/index.dart: -------------------------------------------------------------------------------- 1 | library member_dynamics; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_like/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MemberLikeController extends GetxController {} 4 | -------------------------------------------------------------------------------- /lib/pages/member_like/index.dart: -------------------------------------------------------------------------------- 1 | library member_like; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_like/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MemberLikePage extends StatefulWidget { 4 | const MemberLikePage({super.key}); 5 | 6 | @override 7 | State createState() => _MemberLikePageState(); 8 | } 9 | 10 | class _MemberLikePageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/pages/member_search/index.dart: -------------------------------------------------------------------------------- 1 | library member_search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/member_seasons/index.dart: -------------------------------------------------------------------------------- 1 | library member_seasons; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/message/at/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | class MessageAtController extends GetxController {} 4 | -------------------------------------------------------------------------------- /lib/pages/message/at/index.dart: -------------------------------------------------------------------------------- 1 | library message_at; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/message/at/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MessageAtPage extends StatefulWidget { 4 | const MessageAtPage({super.key}); 5 | 6 | @override 7 | State createState() => _MessageAtPageState(); 8 | } 9 | 10 | class _MessageAtPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: const Text('@我的'), 16 | ), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/message/like/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:pilipala/http/msg.dart'; 3 | import 'package:pilipala/models/msg/like.dart'; 4 | 5 | class MessageLikeController extends GetxController { 6 | Cursor? cursor; 7 | RxList likeItems = [].obs; 8 | 9 | Future queryMessageLike({String type = 'init'}) async { 10 | if (cursor != null && cursor!.isEnd == true) { 11 | return {}; 12 | } 13 | var params = { 14 | if (type == 'onLoad') 'id': cursor!.id, 15 | if (type == 'onLoad') 'likeTime': cursor!.time, 16 | }; 17 | var res = await MsgHttp.messageLike( 18 | id: params['id'], likeTime: params['likeTime']); 19 | if (res['status']) { 20 | cursor = res['data'].total.cursor; 21 | likeItems.addAll(res['data'].total.items); 22 | } 23 | return res; 24 | } 25 | 26 | Future expandedUsersAvatar(i) async { 27 | likeItems[i].isExpand = !likeItems[i].isExpand; 28 | likeItems.refresh(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/pages/message/like/index.dart: -------------------------------------------------------------------------------- 1 | library message_like; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/message/reply/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:pilipala/http/msg.dart'; 3 | import 'package:pilipala/models/msg/reply.dart'; 4 | 5 | class MessageReplyController extends GetxController { 6 | Cursor? cursor; 7 | RxList replyItems = [].obs; 8 | 9 | Future queryMessageReply({String type = 'init'}) async { 10 | if (cursor != null && cursor!.isEnd == true) { 11 | return {}; 12 | } 13 | var params = { 14 | if (type == 'onLoad') 'id': cursor!.id, 15 | if (type == 'onLoad') 'replyTime': cursor!.time, 16 | }; 17 | var res = await MsgHttp.messageReply( 18 | id: params['id'], replyTime: params['replyTime']); 19 | if (res['status']) { 20 | cursor = res['data'].cursor; 21 | replyItems.addAll(res['data'].items); 22 | } 23 | return res; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/pages/message/reply/index.dart: -------------------------------------------------------------------------------- 1 | library message_reply; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/message/system/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/http/msg.dart'; 4 | import 'package:pilipala/models/msg/system.dart'; 5 | 6 | class MessageSystemController extends GetxController { 7 | RxList systemItems = [].obs; 8 | 9 | Future queryAndProcessMessages({String type = 'init'}) async { 10 | // 并行调用两个接口 11 | var results = await Future.wait([ 12 | queryMessageSystem(type: type), 13 | queryMessageSystemAccount(type: type), 14 | ]); 15 | 16 | // 对返回的数据进行处理 17 | var systemRes = results[0]; 18 | var accountRes = results[1]; 19 | 20 | if (systemRes['status'] || accountRes['status']) { 21 | // 处理返回的数据 22 | List combinedData = [ 23 | ...systemRes['data'], 24 | ...accountRes['data'] 25 | ]; 26 | combinedData.sort((a, b) => b.cursor!.compareTo(a.cursor!)); 27 | systemItems.addAll(combinedData); 28 | systemItems.refresh(); 29 | if (systemItems.isNotEmpty) { 30 | systemMarkRead(systemItems.first.cursor!); 31 | } 32 | } else { 33 | SmartDialog.showToast(systemRes['msg'] ?? accountRes['msg']); 34 | } 35 | return systemRes; 36 | } 37 | 38 | // 获取系统消息 39 | Future queryMessageSystem({String type = 'init'}) async { 40 | var res = await MsgHttp.messageSystem(); 41 | return res; 42 | } 43 | 44 | // 获取系统消息 个人 45 | Future queryMessageSystemAccount({String type = 'init'}) async { 46 | var res = await MsgHttp.messageSystemAccount(); 47 | return res; 48 | } 49 | 50 | // 标记已读 51 | void systemMarkRead(int cursor) async { 52 | await MsgHttp.systemMarkRead(cursor); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/pages/message/system/index.dart: -------------------------------------------------------------------------------- 1 | library message_system; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/mine/index.dart: -------------------------------------------------------------------------------- 1 | library mine; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/opus/index.dart: -------------------------------------------------------------------------------- 1 | library opus; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/rank/controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:hive/hive.dart'; 6 | import 'package:pilipala/models/common/rank_type.dart'; 7 | import 'package:pilipala/pages/rank/zone/index.dart'; 8 | import 'package:pilipala/utils/storage.dart'; 9 | 10 | class RankController extends GetxController with GetTickerProviderStateMixin { 11 | bool flag = false; 12 | late RxList tabs = [].obs; 13 | RxInt initialIndex = 0.obs; 14 | late TabController tabController; 15 | late List tabsCtrList; 16 | late List tabsPageList; 17 | Box setting = GStrorage.setting; 18 | late final StreamController searchBarStream = 19 | StreamController.broadcast(); 20 | 21 | @override 22 | void onInit() { 23 | super.onInit(); 24 | // 进行tabs配置 25 | setTabConfig(); 26 | } 27 | 28 | void onRefresh() { 29 | int index = tabController.index; 30 | final ZoneController ctr = tabsCtrList[index]; 31 | ctr.onRefresh(); 32 | } 33 | 34 | void animateToTop() { 35 | int index = tabController.index; 36 | final ZoneController ctr = tabsCtrList[index]; 37 | ctr.animateToTop(); 38 | } 39 | 40 | void setTabConfig() async { 41 | tabs.value = tabsConfig; 42 | initialIndex.value = 0; 43 | tabsCtrList = tabs 44 | .map((e) => Get.put(ZoneController(), tag: e['rid'].toString())) 45 | .toList(); 46 | tabsPageList = tabs.map((e) => e['page']).toList(); 47 | 48 | tabController = TabController( 49 | initialIndex: initialIndex.value, 50 | length: tabs.length, 51 | vsync: this, 52 | ); 53 | } 54 | 55 | @override 56 | void onClose() { 57 | searchBarStream.close(); 58 | super.onClose(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/pages/rank/index.dart: -------------------------------------------------------------------------------- 1 | library rank; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/rank/zone/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:pilipala/http/video.dart'; 4 | import 'package:pilipala/models/model_hot_video_item.dart'; 5 | 6 | class ZoneController extends GetxController { 7 | final ScrollController scrollController = ScrollController(); 8 | RxList videoList = [].obs; 9 | bool isLoadingMore = false; 10 | bool flag = false; 11 | OverlayEntry? popupDialog; 12 | int zoneID = 0; 13 | 14 | // 获取推荐 15 | Future queryRankFeed(type, rid) async { 16 | zoneID = rid; 17 | var res = await VideoHttp.getRankVideoList(zoneID); 18 | if (res['status']) { 19 | if (type == 'init') { 20 | videoList.value = res['data']; 21 | } else if (type == 'onRefresh') { 22 | videoList.clear(); 23 | videoList.addAll(res['data']); 24 | } else if (type == 'onLoad') { 25 | videoList.clear(); 26 | videoList.addAll(res['data']); 27 | } 28 | } 29 | isLoadingMore = false; 30 | return res; 31 | } 32 | 33 | // 下拉刷新 34 | Future onRefresh() async { 35 | queryRankFeed('onRefresh', zoneID); 36 | } 37 | 38 | // 上拉加载 39 | Future onLoad() async { 40 | queryRankFeed('onLoad', zoneID); 41 | } 42 | 43 | // 返回顶部并刷新 44 | void animateToTop() async { 45 | if (scrollController.hasClients) { 46 | if (scrollController.offset >= 47 | MediaQuery.of(Get.context!).size.height * 5) { 48 | scrollController.jumpTo(0); 49 | } else { 50 | await scrollController.animateTo(0, 51 | duration: const Duration(milliseconds: 500), 52 | curve: Curves.easeInOut); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/pages/rank/zone/index.dart: -------------------------------------------------------------------------------- 1 | library rank.zone; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/rcmd/index.dart: -------------------------------------------------------------------------------- 1 | library recm_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/read/index.dart: -------------------------------------------------------------------------------- 1 | library read; 2 | 3 | export 'controller.dart'; 4 | export 'view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search/index.dart: -------------------------------------------------------------------------------- 1 | library search; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search/widgets/search_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SearchText extends StatelessWidget { 4 | final String? searchText; 5 | final Function? onSelect; 6 | final int? searchTextIdx; 7 | final Function? onLongSelect; 8 | final bool isSelect; 9 | const SearchText({ 10 | super.key, 11 | this.searchText, 12 | this.onSelect, 13 | this.searchTextIdx, 14 | this.onLongSelect, 15 | this.isSelect = false, 16 | }); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Material( 21 | color: isSelect 22 | ? Theme.of(context).colorScheme.primaryContainer 23 | : Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), 24 | borderRadius: BorderRadius.circular(6), 25 | child: Padding( 26 | padding: EdgeInsets.zero, 27 | child: InkWell( 28 | onTap: () { 29 | onSelect!(searchText); 30 | }, 31 | onLongPress: () { 32 | onLongSelect!(searchText); 33 | }, 34 | borderRadius: BorderRadius.circular(6), 35 | child: Padding( 36 | padding: 37 | const EdgeInsets.only(top: 5, bottom: 5, left: 11, right: 11), 38 | child: Text( 39 | searchText!, 40 | style: TextStyle( 41 | color: isSelect 42 | ? Theme.of(context).colorScheme.primary 43 | : Theme.of(context).colorScheme.onSurfaceVariant, 44 | ), 45 | ), 46 | ), 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/pages/search_panel/index.dart: -------------------------------------------------------------------------------- 1 | library searchpanel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/search_result/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:pilipala/http/search.dart'; 3 | import 'package:pilipala/models/common/search_type.dart'; 4 | 5 | class SearchResultController extends GetxController { 6 | String? keyword; 7 | int tabIndex = 0; 8 | RxList searchTabs = [].obs; 9 | 10 | @override 11 | void onInit() { 12 | super.onInit(); 13 | if (Get.parameters.keys.isNotEmpty) { 14 | keyword = Get.parameters['keyword']; 15 | } 16 | searchTabs.value = SearchType.values 17 | .map((type) => {'label': type.label, 'id': type.type}) 18 | .toList(); 19 | querySearchCount(); 20 | } 21 | 22 | Future querySearchCount() async { 23 | var result = await SearchHttp.searchCount(keyword: keyword!); 24 | if (result['status']) { 25 | for (var i in searchTabs) { 26 | final count = result['data'].topTList[i['id']]; 27 | i['count'] = count > 99 ? '99+' : count.toString(); 28 | } 29 | searchTabs.refresh(); 30 | } 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/pages/search_result/index.dart: -------------------------------------------------------------------------------- 1 | library searchresult; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/setting/index.dart: -------------------------------------------------------------------------------- 1 | library setting; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/setting/widgets/select_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SelectDialog extends StatefulWidget { 4 | final T value; 5 | final String title; 6 | final List values; 7 | const SelectDialog( 8 | {super.key, 9 | required this.value, 10 | required this.values, 11 | required this.title}); 12 | 13 | @override 14 | _SelectDialogState createState() => _SelectDialogState(); 15 | } 16 | 17 | class _SelectDialogState extends State> { 18 | late T _tempValue; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _tempValue = widget.value; 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!; 29 | 30 | return AlertDialog( 31 | title: Text(widget.title), 32 | contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), 33 | content: StatefulBuilder(builder: (context, StateSetter setState) { 34 | return SingleChildScrollView( 35 | child: Column( 36 | mainAxisSize: MainAxisSize.min, 37 | children: [ 38 | for (var i in widget.values) ...[ 39 | RadioListTile( 40 | value: i['value'], 41 | title: Text(i['title'], style: titleStyle), 42 | groupValue: _tempValue, 43 | onChanged: (value) { 44 | setState(() { 45 | _tempValue = value as T; 46 | }); 47 | Navigator.pop(context, _tempValue); 48 | }, 49 | ), 50 | ] 51 | ], 52 | ), 53 | ); 54 | }), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/pages/subscription/index.dart: -------------------------------------------------------------------------------- 1 | library sub; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/subscription_detail/index.dart: -------------------------------------------------------------------------------- 1 | library sub_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/README.md: -------------------------------------------------------------------------------- 1 | 视频详情页预渲染 2 | + videoItem 3 | + title 4 | + stat 5 | + view 6 | + danmaku 7 | + pubdate 8 | + owner 9 | + face 10 | + name -------------------------------------------------------------------------------- /lib/pages/video/detail/index.dart: -------------------------------------------------------------------------------- 1 | library video_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/introduction/index.dart: -------------------------------------------------------------------------------- 1 | library video_intro_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/related/controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:pilipala/http/video.dart'; 4 | import '../../../../models/model_hot_video_item.dart'; 5 | 6 | class ReleatedController extends GetxController { 7 | // 视频aid 8 | String bvid = Get.parameters['bvid'] ?? ""; 9 | // 推荐视频列表 10 | RxList relatedVideoList = [].obs; 11 | 12 | OverlayEntry? popupDialog; 13 | 14 | Future queryRelatedVideo() async { 15 | return VideoHttp.relatedVideoList(bvid: bvid).then((value) { 16 | if (value['status']) { 17 | relatedVideoList.value = value['data']; 18 | } 19 | return value; 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/pages/video/detail/related/index.dart: -------------------------------------------------------------------------------- 1 | library releated_video_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/reply/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/reply_new/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_new; 2 | 3 | export './view.dart'; -------------------------------------------------------------------------------- /lib/pages/video/detail/reply_new/toolbar_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ToolbarIconButton extends StatelessWidget { 4 | final VoidCallback onPressed; 5 | final Icon icon; 6 | final String toolbarType; 7 | final bool selected; 8 | 9 | const ToolbarIconButton({ 10 | super.key, 11 | required this.onPressed, 12 | required this.icon, 13 | required this.toolbarType, 14 | required this.selected, 15 | }); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return SizedBox( 20 | width: 36, 21 | height: 36, 22 | child: IconButton( 23 | onPressed: onPressed, 24 | icon: icon, 25 | highlightColor: Theme.of(context).colorScheme.secondaryContainer, 26 | color: selected 27 | ? Theme.of(context).colorScheme.onSecondaryContainer 28 | : Theme.of(context).colorScheme.outline, 29 | style: ButtonStyle( 30 | padding: MaterialStateProperty.all(EdgeInsets.zero), 31 | backgroundColor: MaterialStateProperty.resolveWith((states) { 32 | return selected 33 | ? Theme.of(context).colorScheme.secondaryContainer 34 | : null; 35 | }), 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/pages/video/detail/reply_reply/index.dart: -------------------------------------------------------------------------------- 1 | library video_reply_reply_panel; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/video/detail/widgets/right_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RightDrawer extends StatefulWidget { 4 | const RightDrawer({super.key}); 5 | 6 | @override 7 | State createState() => _RightDrawerState(); 8 | } 9 | 10 | class _RightDrawerState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Drawer( 14 | shadowColor: Colors.transparent, 15 | elevation: 0, 16 | backgroundColor: 17 | Theme.of(context).colorScheme.surface.withOpacity(0.8)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/webview/index.dart: -------------------------------------------------------------------------------- 1 | library webview; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/whisper/index.dart: -------------------------------------------------------------------------------- 1 | library whisper; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/pages/whisper_detail/index.dart: -------------------------------------------------------------------------------- 1 | library whisper_detail; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | -------------------------------------------------------------------------------- /lib/plugin/pl_gallery/hero_dialog_route.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// A [PageRoute] with a semi transparent background. 4 | /// 5 | /// Similar to calling [showDialog] except it can be used with a [Navigator] to 6 | /// show a [Hero] animation. 7 | class HeroDialogRoute extends PageRoute { 8 | HeroDialogRoute({ 9 | required this.builder, 10 | this.onBackgroundTap, 11 | }) : super(); 12 | 13 | final WidgetBuilder builder; 14 | 15 | /// Called when the background is tapped. 16 | final VoidCallback? onBackgroundTap; 17 | 18 | @override 19 | bool get opaque => false; 20 | 21 | @override 22 | bool get barrierDismissible => true; 23 | 24 | @override 25 | String? get barrierLabel => null; 26 | 27 | @override 28 | Duration get transitionDuration => const Duration(milliseconds: 300); 29 | 30 | @override 31 | bool get maintainState => true; 32 | 33 | @override 34 | Color? get barrierColor => null; 35 | 36 | @override 37 | Widget buildTransitions( 38 | BuildContext context, 39 | Animation animation, 40 | Animation secondaryAnimation, 41 | Widget child, 42 | ) { 43 | return FadeTransition( 44 | opacity: CurvedAnimation(parent: animation, curve: Curves.easeOut), 45 | child: child, 46 | ); 47 | } 48 | 49 | @override 50 | Widget buildPage( 51 | BuildContext context, 52 | Animation animation, 53 | Animation secondaryAnimation, 54 | ) { 55 | final Widget child = builder(context); 56 | final Widget result = Semantics( 57 | scopesRoute: true, 58 | explicitChildNodes: true, 59 | child: child, 60 | ); 61 | return result; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/plugin/pl_gallery/index.dart: -------------------------------------------------------------------------------- 1 | library pl_gallery; 2 | 3 | export './hero_dialog_route.dart'; 4 | export './custom_dismissible.dart'; 5 | export './interactiveviewer_gallery.dart'; 6 | export './interactive_viewer_boundary.dart'; 7 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/index.dart: -------------------------------------------------------------------------------- 1 | library pl_player; 2 | 3 | export './controller.dart'; 4 | export './view.dart'; 5 | export './models/data_source.dart'; 6 | export './models/play_status.dart'; 7 | export './models/data_status.dart'; 8 | export './widgets/common_btn.dart'; 9 | export './models/play_speed.dart'; 10 | export './models/fullscreen_mode.dart'; 11 | export './models/bottom_progress_behavior.dart'; 12 | export './widgets/app_bar_ani.dart'; 13 | export './utils/fullscreen.dart'; 14 | export './utils.dart'; 15 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/bottom_control_type.dart: -------------------------------------------------------------------------------- 1 | enum BottomControlType { 2 | pre, 3 | playOrPause, 4 | next, 5 | time, 6 | space, 7 | episode, 8 | fit, 9 | speed, 10 | fullscreen, 11 | custom, 12 | } 13 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/bottom_progress_behavior.dart: -------------------------------------------------------------------------------- 1 | // ignore: camel_case_types 2 | enum BtmProgresBehavior { 3 | alwaysShow, 4 | alwaysHide, 5 | onlyShowFullScreen, 6 | onlyHideFullScreen, 7 | } 8 | 9 | extension BtmProgresBehaviorDesc on BtmProgresBehavior { 10 | String get description => ['始终展示', '始终隐藏', '仅全屏时展示', '仅全屏时隐藏'][index]; 11 | } 12 | 13 | extension BtmProgresBehaviorCode on BtmProgresBehavior { 14 | static final List _codeList = [0, 1, 2, 3]; 15 | int get code => _codeList[index]; 16 | 17 | static BtmProgresBehavior? fromCode(int code) { 18 | final index = _codeList.indexOf(code); 19 | if (index != -1) { 20 | return BtmProgresBehavior.values[index]; 21 | } 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/data_source.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | /// The way in which the video was originally loaded. 4 | /// 5 | /// This has nothing to do with the video's file type. It's just the place 6 | /// from which the video is fetched from. 7 | enum DataSourceType { 8 | /// The video was included in the app's asset files. 9 | asset, 10 | 11 | /// The video was downloaded from the internet. 12 | network, 13 | 14 | /// The video was loaded off of the local filesystem. 15 | file, 16 | 17 | /// The video is available via contentUri. Android only. 18 | contentUri, 19 | } 20 | 21 | class DataSource { 22 | File? file; 23 | String? videoSource; 24 | String? audioSource; 25 | String? subFiles; 26 | DataSourceType type; 27 | Map? httpHeaders; // for headers 28 | DataSource({ 29 | this.file, 30 | this.videoSource, 31 | this.audioSource, 32 | this.subFiles, 33 | required this.type, 34 | this.httpHeaders, 35 | }) : assert((type == DataSourceType.file && file != null) || 36 | videoSource != null); 37 | 38 | DataSource copyWith({ 39 | File? file, 40 | String? videoSource, 41 | String? audioSource, 42 | String? subFiles, 43 | DataSourceType? type, 44 | Map? httpHeaders, 45 | }) { 46 | return DataSource( 47 | file: file ?? this.file, 48 | videoSource: videoSource ?? this.videoSource, 49 | audioSource: audioSource ?? this.audioSource, 50 | subFiles: subFiles ?? this.subFiles, 51 | type: type ?? this.type, 52 | httpHeaders: httpHeaders ?? this.httpHeaders, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/data_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | enum DataStatus { none, loading, loaded, error } 4 | 5 | class PlPlayerDataStatus { 6 | Rx status = Rx(DataStatus.none); 7 | 8 | bool get none => status.value == DataStatus.none; 9 | bool get loading => status.value == DataStatus.loading; 10 | bool get loaded => status.value == DataStatus.loaded; 11 | bool get error => status.value == DataStatus.error; 12 | } 13 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/duration.dart: -------------------------------------------------------------------------------- 1 | extension DurationExtension on Duration { 2 | /// Returns clamp of [Duration] between [min] and [max]. 3 | Duration clamp(Duration min, Duration max) { 4 | if (this < min) return min; 5 | if (this > max) return max; 6 | return this; 7 | } 8 | 9 | /// Returns a [String] representation of [Duration]. 10 | String label({Duration? reference}) { 11 | reference ??= this; 12 | if (reference > const Duration(days: 1)) { 13 | final days = inDays.toString().padLeft(3, '0'); 14 | final hours = (inHours - (inDays * 24)).toString().padLeft(2, '0'); 15 | final minutes = (inMinutes - (inHours * 60)).toString().padLeft(2, '0'); 16 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 17 | return '$days:$hours:$minutes:$seconds'; 18 | } else if (reference > const Duration(hours: 1)) { 19 | final hours = inHours.toString().padLeft(2, '0'); 20 | final minutes = (inMinutes - (inHours * 60)).toString().padLeft(2, '0'); 21 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 22 | return '$hours:$minutes:$seconds'; 23 | } else { 24 | final minutes = inMinutes.toString().padLeft(2, '0'); 25 | final seconds = (inSeconds - (inMinutes * 60)).toString().padLeft(2, '0'); 26 | return '$minutes:$seconds'; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/fullscreen_mode.dart: -------------------------------------------------------------------------------- 1 | // 全屏模式 2 | enum FullScreenMode { 3 | // 根据内容自适应 4 | auto, 5 | // 始终竖屏 6 | vertical, 7 | // 始终横屏 8 | horizontal 9 | } 10 | 11 | extension FullScreenModeDesc on FullScreenMode { 12 | String get description => ['自适应', '始终竖屏', '始终横屏'][index]; 13 | } 14 | 15 | extension FullScreenModeCode on FullScreenMode { 16 | static final List _codeList = [0, 1, 2]; 17 | int get code => _codeList[index]; 18 | 19 | static FullScreenMode? fromCode(int code) { 20 | final index = _codeList.indexOf(code); 21 | if (index != -1) { 22 | return FullScreenMode.values[index]; 23 | } 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_repeat.dart: -------------------------------------------------------------------------------- 1 | enum PlayRepeat { 2 | pause, 3 | listOrder, 4 | singleCycle, 5 | listCycle, 6 | } 7 | 8 | extension PlayRepeatExtension on PlayRepeat { 9 | static final List _descList = [ 10 | '播完暂停', 11 | '顺序播放', 12 | '单个循环', 13 | '列表循环', 14 | ]; 15 | String get description => _descList[index]; 16 | 17 | static final List _valueList = [ 18 | 1, 19 | 2, 20 | 3, 21 | 4, 22 | ]; 23 | double get value => _valueList[index]; 24 | double get defaultValue => _valueList[1]; 25 | } 26 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_speed.dart: -------------------------------------------------------------------------------- 1 | List generatePlaySpeedList() { 2 | List playSpeed = []; 3 | double startSpeed = 0.25; 4 | double endSpeed = 2.0; 5 | double increment = 0.25; 6 | 7 | for (double speed = startSpeed; speed <= endSpeed; speed += increment) { 8 | playSpeed.add(speed); 9 | } 10 | 11 | return playSpeed; 12 | } 13 | 14 | // 导出 playSpeed 列表 15 | List playSpeed = generatePlaySpeedList(); 16 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/models/play_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | enum PlayerStatus { completed, playing, paused } 4 | 5 | class PlPlayerStatus { 6 | Rx status = Rx(PlayerStatus.paused); 7 | 8 | bool get playing { 9 | return status.value == PlayerStatus.playing; 10 | } 11 | 12 | bool get paused { 13 | return status.value == PlayerStatus.paused; 14 | } 15 | 16 | bool get completed { 17 | return status.value == PlayerStatus.completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/utils.dart: -------------------------------------------------------------------------------- 1 | String printDuration(Duration? duration) { 2 | if (duration == null) return "--:--"; 3 | 4 | /*String twoDigits(int n) { 5 | if (n >= 10||n < 0) return "$n"; 6 | return "0$n"; 7 | }*/ 8 | String twoDigits(int n) => n.toString().padLeft(2, "0"); 9 | 10 | String twoDigitMinutes = twoDigits(duration.inMinutes).replaceAll("-", ""); 11 | String twoDigitSeconds = 12 | twoDigits(duration.inSeconds.remainder(60)).replaceAll("-", ""); 13 | //customDebugPrint(duration.inSeconds.remainder(60)); 14 | return "$twoDigitMinutes:$twoDigitSeconds"; 15 | } 16 | 17 | String printDurationWithHours(Duration? duration) { 18 | if (duration == null) return "--:--:--"; 19 | 20 | String twoDigits(int n) { 21 | if (n >= 10) return "$n"; 22 | return "0$n"; 23 | } 24 | 25 | String twoDigitHours = twoDigits(duration.inHours); 26 | String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); 27 | String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); 28 | return "$twoDigitHours:$twoDigitMinutes:$twoDigitSeconds"; 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/widgets/common_btn.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ComBtn extends StatelessWidget { 4 | final Widget? icon; 5 | final Function? fuc; 6 | 7 | const ComBtn({ 8 | this.icon, 9 | this.fuc, 10 | super.key, 11 | }); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SizedBox( 16 | width: 34, 17 | height: 34, 18 | child: IconButton( 19 | style: ButtonStyle( 20 | padding: MaterialStateProperty.all(EdgeInsets.zero), 21 | ), 22 | onPressed: () { 23 | fuc!(); 24 | }, 25 | icon: icon!, 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/plugin/pl_player/widgets/control_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ControlBar extends StatelessWidget { 4 | final bool visible; 5 | final IconData icon; 6 | final double value; 7 | 8 | const ControlBar({ 9 | Key? key, 10 | required this.visible, 11 | required this.icon, 12 | required this.value, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | Color color = const Color(0xFFFFFFFF); 18 | return Align( 19 | child: AnimatedOpacity( 20 | curve: Curves.easeInOut, 21 | opacity: visible ? 1.0 : 0.0, 22 | duration: const Duration(milliseconds: 150), 23 | child: IntrinsicWidth( 24 | child: Container( 25 | padding: const EdgeInsets.fromLTRB(10, 2, 10, 2), 26 | decoration: BoxDecoration( 27 | color: const Color(0x88000000), 28 | borderRadius: BorderRadius.circular(64.0), 29 | ), 30 | height: 34.0, 31 | child: Row( 32 | children: [ 33 | Icon(icon, color: color, size: 18.0), 34 | const SizedBox(width: 4.0), 35 | Container( 36 | constraints: const BoxConstraints(minWidth: 30.0), 37 | child: Text( 38 | '${(value * 100.0).round()}%', 39 | textAlign: TextAlign.center, 40 | style: TextStyle(fontSize: 13.0, color: color), 41 | ), 42 | ) 43 | ], 44 | ), 45 | ), 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/plugin/pl_popup/index.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PlPopupRoute extends PopupRoute { 4 | PlPopupRoute({ 5 | this.backgroudColor, 6 | this.alignment = Alignment.center, 7 | required this.child, 8 | this.onClick, 9 | }); 10 | 11 | /// backgroudColor 12 | final Color? backgroudColor; 13 | 14 | /// child'alignment, default value: [Alignment.center] 15 | final Alignment alignment; 16 | 17 | /// child 18 | final Widget child; 19 | 20 | /// backgroudView action 21 | final Function? onClick; 22 | 23 | @override 24 | Duration get transitionDuration => const Duration(milliseconds: 300); 25 | 26 | @override 27 | bool get barrierDismissible => false; 28 | 29 | @override 30 | Color get barrierColor => Colors.black54; 31 | 32 | @override 33 | String? get barrierLabel => null; 34 | 35 | @override 36 | Widget buildPage( 37 | BuildContext context, 38 | Animation animation, 39 | Animation secondaryAnimation, 40 | ) { 41 | return child; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/scripts/build.sh: -------------------------------------------------------------------------------- 1 | flutter build apk --target-platform android-arm64 --split-per-abi -------------------------------------------------------------------------------- /lib/services/disable_battery_opt.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:disable_battery_optimization/disable_battery_optimization.dart'; 4 | import 'package:pilipala/utils/storage.dart'; 5 | 6 | void DisableBatteryOpt() async { 7 | if (!Platform.isAndroid) { 8 | return; 9 | } 10 | // 本地缓存中读取 是否禁用了电池优化 默认未禁用 11 | bool isDisableBatteryOptLocal = 12 | GStrorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false); 13 | if (!isDisableBatteryOptLocal) { 14 | final isBatteryOptimizationDisabled = 15 | await DisableBatteryOptimization.isBatteryOptimizationDisabled; 16 | if (isBatteryOptimizationDisabled == false) { 17 | final hasDisabled = await DisableBatteryOptimization 18 | .showDisableBatteryOptimizationSettings(); 19 | // 设置为已禁用 20 | GStrorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true); 21 | } 22 | } 23 | 24 | bool isManufacturerBatteryOptimizationDisabled = GStrorage.localCache 25 | .get('isManufacturerBatteryOptimizationDisabled', defaultValue: false); 26 | if (!isManufacturerBatteryOptimizationDisabled) { 27 | final isManBatteryOptimizationDisabled = await DisableBatteryOptimization 28 | .isManufacturerBatteryOptimizationDisabled; 29 | if (isManBatteryOptimizationDisabled == false) { 30 | final hasDisabled = await DisableBatteryOptimization 31 | .showDisableManufacturerBatteryOptimizationSettings( 32 | "当前设备可能有额外的电池优化", 33 | "按照步骤操作以禁用电池优化,以保证应用在后台正常运行", 34 | ); 35 | // 设置为已禁用 36 | GStrorage.localCache.put( 37 | 'isManufacturerBatteryOptimizationDisabled', hasDisabled == true); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/services/loggeer.dart: -------------------------------------------------------------------------------- 1 | // final _loggerFactory = 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:logger/logger.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:path/path.dart' as p; 8 | 9 | final _loggerFactory = PiliLogger(); 10 | 11 | PiliLogger getLogger() { 12 | return _loggerFactory; 13 | } 14 | 15 | class PiliLogger extends Logger { 16 | PiliLogger() : super(); 17 | 18 | @override 19 | void log(Level level, dynamic message, 20 | {Object? error, StackTrace? stackTrace, DateTime? time}) async { 21 | if (level == Level.error) { 22 | String dir = (await getApplicationDocumentsDirectory()).path; 23 | // 创建logo文件 24 | final String filename = p.join(dir, ".pili_logs"); 25 | // 添加至文件末尾 26 | await File(filename).writeAsString( 27 | "**${DateTime.now()}** \n $message \n $stackTrace", 28 | mode: FileMode.writeOnlyAppend, 29 | ); 30 | } 31 | super.log(level, "$message", error: error, stackTrace: stackTrace); 32 | } 33 | } 34 | 35 | Future getLogsPath() async { 36 | String dir = (await getApplicationDocumentsDirectory()).path; 37 | final String filename = p.join(dir, ".pili_logs"); 38 | final file = File(filename); 39 | if (!await file.exists()) { 40 | await file.create(); 41 | } 42 | return file; 43 | } 44 | 45 | Future clearLogs() async { 46 | String dir = (await getApplicationDocumentsDirectory()).path; 47 | final String filename = p.join(dir, ".pili_logs"); 48 | final file = File(filename); 49 | try { 50 | await file.writeAsString(''); 51 | } catch (e) { 52 | print('Error clearing file: $e'); 53 | return false; 54 | } 55 | return true; 56 | } 57 | -------------------------------------------------------------------------------- /lib/services/service_locator.dart: -------------------------------------------------------------------------------- 1 | import 'audio_handler.dart'; 2 | import 'audio_session.dart'; 3 | 4 | late VideoPlayerServiceHandler videoPlayerServiceHandler; 5 | late AudioSessionHandler audioSessionHandler; 6 | 7 | Future setupServiceLocator() async { 8 | final audio = await initAudioService(); 9 | videoPlayerServiceHandler = audio; 10 | audioSessionHandler = AudioSessionHandler(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/utils/cookie.dart: -------------------------------------------------------------------------------- 1 | import 'package:pilipala/http/constants.dart'; 2 | import 'package:pilipala/http/init.dart'; 3 | import 'package:webview_cookie_manager/webview_cookie_manager.dart'; 4 | 5 | class SetCookie { 6 | static onSet() async { 7 | var cookies = await WebviewCookieManager().getCookies(HttpString.baseUrl); 8 | await Request.cookieManager.cookieJar 9 | .saveFromResponse(Uri.parse(HttpString.baseUrl), cookies); 10 | var cookieString = 11 | cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; '); 12 | Request.dio.options.headers['cookie'] = cookieString; 13 | 14 | cookies = await WebviewCookieManager().getCookies(HttpString.apiBaseUrl); 15 | await Request.cookieManager.cookieJar 16 | .saveFromResponse(Uri.parse(HttpString.apiBaseUrl), cookies); 17 | 18 | cookies = await WebviewCookieManager().getCookies(HttpString.tUrl); 19 | await Request.cookieManager.cookieJar 20 | .saveFromResponse(Uri.parse(HttpString.tUrl), cookies); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/utils/danmaku.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:ns_danmaku/ns_danmaku.dart'; 3 | 4 | class DmUtils { 5 | static Color decimalToColor(int decimalColor) { 6 | // 16777215 表示白色 7 | int red = (decimalColor >> 16) & 0xFF; 8 | int green = (decimalColor >> 8) & 0xFF; 9 | int blue = decimalColor & 0xFF; 10 | 11 | return Color.fromARGB(255, red, green, blue); 12 | } 13 | 14 | static DanmakuItemType getPosition(int mode) { 15 | DanmakuItemType type = DanmakuItemType.scroll; 16 | if (mode >= 1 && mode <= 3) { 17 | type = DanmakuItemType.scroll; 18 | } else if (mode == 4) { 19 | type = DanmakuItemType.bottom; 20 | } else if (mode == 5) { 21 | type = DanmakuItemType.top; 22 | } 23 | return type; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/utils/data.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:pilipala/http/user.dart'; 3 | 4 | import 'storage.dart'; 5 | 6 | class Data { 7 | static Future init() async { 8 | await historyStatus(); 9 | } 10 | 11 | static Future historyStatus() async { 12 | Box localCache = GStrorage.localCache; 13 | Box userInfoCache = GStrorage.userInfo; 14 | if (userInfoCache.get('userInfoCache') == null) { 15 | return; 16 | } 17 | var res = await UserHttp.historyStatus(); 18 | localCache.put(LocalCacheKey.historyPause, res.data['data']); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/utils/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 3 | 4 | class DrawerUtils { 5 | static void showRightDialog({ 6 | required Widget child, 7 | double width = 400, 8 | bool useSystem = false, 9 | }) { 10 | SmartDialog.show( 11 | alignment: Alignment.topRight, 12 | animationBuilder: (controller, child, animationParam) { 13 | return SlideTransition( 14 | position: Tween( 15 | begin: const Offset(1, 0), 16 | end: Offset.zero, 17 | ).animate(controller.view), 18 | child: child, 19 | ); 20 | }, 21 | useSystem: useSystem, 22 | maskColor: Colors.black.withOpacity(0.5), 23 | animationTime: const Duration(milliseconds: 200), 24 | builder: (context) => Container( 25 | width: width, 26 | color: Theme.of(context).scaffoldBackgroundColor, 27 | child: SafeArea( 28 | left: false, 29 | right: false, 30 | bottom: false, 31 | child: MediaQuery( 32 | data: const MediaQueryData(padding: EdgeInsets.zero), 33 | child: child, 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/utils/em.dart: -------------------------------------------------------------------------------- 1 | class Em { 2 | static regCate(String origin) { 3 | String str = origin; 4 | RegExp exp = RegExp('<[^>]*>([^<]*)]*>'); 5 | Iterable matches = exp.allMatches(origin); 6 | for (Match match in matches) { 7 | str = match.group(1)!; 8 | } 9 | return str; 10 | } 11 | 12 | static regTitle(String origin) { 13 | RegExp exp = RegExp('<[^>]*>([^<]*)]*>'); 14 | List res = []; 15 | origin.splitMapJoin(exp, onMatch: (Match match) { 16 | String matchStr = match[0]!; 17 | Map map = {'type': 'em', 'text': regCate(matchStr)}; 18 | res.add(map); 19 | return regCate(matchStr); 20 | }, onNonMatch: (String str) { 21 | if (str != '') { 22 | str = decodeHtmlEntities(str); 23 | Map map = {'type': 'text', 'text': str}; 24 | res.add(map); 25 | } 26 | return str; 27 | }); 28 | return res; 29 | } 30 | 31 | static String decodeHtmlEntities(String title) { 32 | return title 33 | .replaceAll('<', '<') 34 | .replaceAll('>', '>') 35 | .replaceAll('"', '"') 36 | .replaceAll(''', "'") 37 | .replaceAll('"', '"') 38 | .replaceAll(''', "'") 39 | .replaceAll(' ', " ") 40 | .replaceAll('&', "&") 41 | .replaceAll(''', "'"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/utils/event_bus.dart: -------------------------------------------------------------------------------- 1 | // 订阅者回调签名 2 | typedef void EventCallback(arg); 3 | 4 | class EventBus { 5 | // 私有构造函数 6 | EventBus._internal(); 7 | 8 | // 保存单例 9 | static final EventBus _singleton = EventBus._internal(); 10 | 11 | // 工厂构造函数 12 | factory EventBus() => _singleton; 13 | 14 | // 保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列 15 | final _emap = >{}; 16 | 17 | // 添加订阅者 18 | void on(eventName, EventCallback f) { 19 | _emap[eventName] ??= []; 20 | _emap[eventName]!.add(f); 21 | } 22 | 23 | // 移除订阅者 24 | void off(eventName, [EventCallback? f]) { 25 | var list = _emap[eventName]; 26 | if (eventName == null || list == null) return; 27 | if (f == null) { 28 | _emap[eventName] = []; 29 | } else { 30 | list.remove(f); 31 | } 32 | } 33 | 34 | // 触发事件,事件触发后该事件所有订阅者会被调用 35 | void emit(eventName, [arg]) { 36 | var list = _emap[eventName]; 37 | if (list == null) return; 38 | List tempList = List.from(list); 39 | for (var callback in tempList) { 40 | callback(arg); 41 | } 42 | } 43 | 44 | // 获取订阅者数量 45 | int getSubscriberCount(eventName) { 46 | var list = _emap[eventName]; 47 | return list?.length ?? 0; 48 | } 49 | } 50 | 51 | class EventName { 52 | static const String loginEvent = 'loginEvent'; 53 | } 54 | -------------------------------------------------------------------------------- /lib/utils/extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension ImageExtension on num { 4 | int cacheSize(BuildContext context) { 5 | return (this * MediaQuery.of(context).devicePixelRatio).round(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/feed_back.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:hive/hive.dart'; 3 | import 'storage.dart'; 4 | 5 | Box setting = GStrorage.setting; 6 | void feedBack() { 7 | // 设置中是否开启 8 | final bool enable = 9 | setting.get(SettingBoxKey.feedBackEnable, defaultValue: false) as bool; 10 | if (enable) { 11 | HapticFeedback.lightImpact(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/highlight.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:re_highlight/languages/all.dart'; 3 | import 'package:re_highlight/re_highlight.dart'; 4 | import 'package:re_highlight/styles/all.dart'; 5 | 6 | TextSpan? highlightExistingText(String text, List languages) { 7 | final Highlight highlight = Highlight(); 8 | highlight.registerLanguages(builtinAllLanguages); 9 | final HighlightResult result = highlight.highlightAuto(text, languages); 10 | final TextSpanRenderer renderer = 11 | TextSpanRenderer(const TextStyle(), builtinAllThemes['github']!); 12 | result.render(renderer); 13 | return renderer.span; 14 | } 15 | -------------------------------------------------------------------------------- /lib/utils/main_stream.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:easy_debounce/easy_throttle.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | import 'package:get/get.dart'; 6 | 7 | import '../pages/home/index.dart'; 8 | import '../pages/main/index.dart'; 9 | 10 | void handleScrollEvent(ScrollController scrollController) { 11 | StreamController mainStream = 12 | Get.find().bottomBarStream; 13 | StreamController searchBarStream = 14 | Get.find().searchBarStream; 15 | EasyThrottle.throttle( 16 | 'stream-throttler', 17 | const Duration(milliseconds: 300), 18 | () { 19 | try { 20 | final ScrollDirection direction = 21 | scrollController.position.userScrollDirection; 22 | if (direction == ScrollDirection.forward) { 23 | mainStream.add(true); 24 | searchBarStream.add(true); 25 | } else if (direction == ScrollDirection.reverse) { 26 | mainStream.add(false); 27 | searchBarStream.add(false); 28 | } 29 | } catch (_) {} 30 | }, 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /lib/utils/proxy.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:system_proxy/system_proxy.dart'; 3 | 4 | class CustomProxy { 5 | init() async { 6 | Map? proxy = await SystemProxy.getProxySettings(); 7 | if (proxy != null) { 8 | HttpOverrides.global = 9 | ProxiedHttpOverrides(proxy['host']!, proxy['port']!); 10 | } 11 | } 12 | } 13 | 14 | class ProxiedHttpOverrides extends HttpOverrides { 15 | final String _port; 16 | final String _host; 17 | 18 | ProxiedHttpOverrides(this._host, this._port); 19 | 20 | @override 21 | HttpClient createHttpClient(SecurityContext? context) { 22 | return super.createHttpClient(context) 23 | // set proxy 24 | ..findProxy = (uri) { 25 | return "PROXY $_host:$_port;"; 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/utils/video_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:pilipala/models/video/play/url.dart'; 2 | 3 | import '../models/live/room_info.dart'; 4 | 5 | class VideoUtils { 6 | static String getCdnUrl(dynamic item) { 7 | var backupUrl = ""; 8 | var videoUrl = ""; 9 | 10 | /// 先获取backupUrl 一般是upgcxcode地址 播放更稳定 11 | if (item is VideoItem) { 12 | backupUrl = item.backupUrl ?? ""; 13 | videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); 14 | } else if (item is AudioItem) { 15 | backupUrl = item.backupUrl ?? ""; 16 | videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); 17 | } else if (item is CodecItem) { 18 | backupUrl = (item.urlInfo?.first.host)! + 19 | item.baseUrl! + 20 | item.urlInfo!.first.extra!; 21 | videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); 22 | } else { 23 | return ""; 24 | } 25 | 26 | /// issues #70 27 | if (videoUrl.contains(".mcdn.bilivideo")) { 28 | videoUrl = 29 | 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}'; 30 | } else if (videoUrl.contains("/upgcxcode/")) { 31 | //CDN列表 32 | var cdnList = { 33 | 'ali': 'upos-sz-mirrorali.bilivideo.com', 34 | 'cos': 'upos-sz-mirrorcos.bilivideo.com', 35 | 'hw': 'upos-sz-mirrorhw.bilivideo.com', 36 | }; 37 | //取一个CDN 38 | var cdn = cdnList['ali'] ?? ""; 39 | var reg = RegExp(r'(http|https)://(.*?)/upgcxcode/'); 40 | videoUrl = videoUrl.replaceAll(reg, "https://$cdn/upgcxcode/"); 41 | } 42 | 43 | return videoUrl; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = pilipala 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.pilipala 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | com.apple.security.network.client 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:pilipala/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pilipala", 3 | "short_name": "pilipala", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"pilipala", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/666666g/Mosaict/4a7adb3f9038bd6ab2abe4087b5188a7a1f82e41/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------