├── .gitignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.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 │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── 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-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.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 │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ └── main.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── 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 │ └── RunnerTests │ │ └── RunnerTests.swift ├── 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 │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── 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 ├── lib ├── channel.dart ├── enum │ ├── category.dart │ ├── channel │ │ └── channel_part.dart │ ├── playlist │ │ └── playlist_part.dart │ ├── query_parameter.dart │ ├── result_type.dart │ ├── search │ │ ├── channel_type.dart │ │ ├── event_type.dart │ │ ├── order.dart │ │ ├── safe_search.dart │ │ ├── search_part.dart │ │ └── topic.dart │ ├── video │ │ ├── chart.dart │ │ ├── live_broadcast_content.dart │ │ ├── my_rating.dart │ │ ├── video_part.dart │ │ └── video_projection.dart │ ├── video_caption.dart │ ├── video_definition.dart │ ├── video_dimension.dart │ ├── video_duration.dart │ ├── video_embeddable.dart │ ├── video_license.dart │ ├── video_syndicated.dart │ └── video_type.dart ├── model │ ├── channel │ │ ├── branding_settings.dart │ │ ├── channel_options.dart │ │ ├── channel_snippet.dart │ │ └── youtube_channel.dart │ ├── options.dart │ ├── playlist │ │ ├── playlist_options.dart │ │ ├── playlist_snippet.dart │ │ └── youtube_playlist.dart │ ├── search │ │ └── search_options.dart │ ├── snippet.dart │ ├── thumbnails │ │ ├── thumbnail.dart │ │ └── thumbnail_resolution.dart │ ├── video │ │ ├── video_content_details.dart │ │ ├── video_options.dart │ │ ├── video_snippet.dart │ │ └── youtube_video.dart │ └── youtube_api_result.dart ├── playlist.dart ├── search.dart ├── util │ └── duration_extension.dart ├── video.dart └── youtube_api.dart ├── pubspec.yaml ├── screenshots └── demo.jpg ├── test └── youtube_api_test.dart └── youtube_api_client.iml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .dart_tool/ 4 | .idea 5 | .packages 6 | .pub/ 7 | build/ 8 | ios/.generated/ 9 | packages 10 | pubspec.lock 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "youtube_api_client", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "youtube_api_client (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "youtube_api_client (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | }, 24 | { 25 | "name": "example", 26 | "cwd": "example", 27 | "request": "launch", 28 | "type": "dart" 29 | }, 30 | { 31 | "name": "example (profile mode)", 32 | "cwd": "example", 33 | "request": "launch", 34 | "type": "dart", 35 | "flutterMode": "profile" 36 | }, 37 | { 38 | "name": "example (release mode)", 39 | "cwd": "example", 40 | "request": "launch", 41 | "type": "dart", 42 | "flutterMode": "release" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.0] - August 14th 2020 2 | 3 | - Initial version of youtube_api_client. (Starting after youtube_api package fork on version 1.0.3). 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Hyung Tae Carapeto Figur 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YouTube API Client (youtube_api_client) 2 | 3 | Forked (all restructured and improved) from [youtube_api](https://pub.dartlang.org/packages/youtube_api) 4 | 5 | A Flutter plugin for fetching interacting with YouTube Server to fetch data using API. Supports iOS and Android. 6 | 7 | ## Features: 8 | 9 | - Search Video, Playlist, Channel on YouTube (query by keywords or by ID) 10 | - Get Trending Videos based on region code. 11 | 12 | ## Usage 13 | 14 | To use this plugin, add `youtube_api_client` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). 15 | 16 | [Complete Example Code](https://pub.dartlang.org/packages/youtube_api_client#-example-tab-) 17 | 18 | ### Example 19 | 20 | ```dart 21 | static String key = "YOUR_API_KEY"; 22 | final youtube = YoutubeApi(_key); 23 | List result = []; 24 | ``` 25 | 26 | Search for videos, channels and playlists 27 | 28 | ```dart 29 | String query = "Flutter"; 30 | result = await youtube.search(query); 31 | // data which are available in result is typed as in the example shown below 32 | ``` 33 | 34 | By default the search options are like the following: 35 | 36 | ```dart 37 | SearchOptions options = const SearchOptions( 38 | type: ResultType.values, 39 | order: Order.relevance, 40 | videoDuration: VideoDuration.any, 41 | ) 42 | ``` 43 | 44 | But you can customize them changing the parameter options. For example, if you want to get only results for channels, you can specify like so: 45 | 46 | ```dart 47 | SearchOptions(type: ResultType.channel) 48 | ``` 49 | 50 | To get Trending videos in your Country- 51 | 52 | ```dart 53 | regionCode='YOUR_COUNTRY_REGION_CODE(apha-2)'; 54 | result = await youtube.getTrends(regionCode); 55 | //make sure you assign alpha-2 region code 56 | ``` 57 | 58 | To get results by id use `searchVideosById`, `searchChannelsById`, and `searchPlaylistsById`. 59 | 60 | ```dart 61 | result = await youtube.searchVideosById(idList); 62 | ``` 63 | 64 | [You can find your Country Region Code here](https://www.iso.org/obp/ui/#search/code/) 65 | 66 | By default, it retrieves only the "snippet" data, which has enough information for most of the cases. 67 | 68 | For example the snippet for a video contains: 69 | 70 | 1. title (String) 71 | 2. description (String) 72 | 3. publish date (DateTime) 73 | 4. channel ID (String) 74 | 5. channel title (String) 75 | 6. thumbnails (Map - custom classes) 76 | 7. video category (Category - enum) 77 | 8. tags (List) 78 | 9. default language (String) 79 | 10. defaultAudioLanguage (String) 80 | 11. live broadcast content (LiveBroadcastContent - enum) 81 | 82 | If you need more information from the API, you can add other parts in the query. For now it has only the part "snippet" and "content details" (containing: duration, dimension, definition, caption, licensed content, and projection). The original API has lots more of information, so you are welcome to help implementing those making pull requests. 83 | 84 | ## Motivation 85 | 86 | The original package seems to be abandoned. I improved a lot its code, making it more typed, and added more features. 87 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.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: f468f3366c26a5092eb964a230ce7892fda8f2f8 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: f468f3366c26a5092eb964a230ce7892fda8f2f8 17 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 18 | - platform: android 19 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 20 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 21 | - platform: ios 22 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 23 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 24 | - platform: linux 25 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 26 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 27 | - platform: macos 28 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 29 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 30 | - platform: web 31 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 32 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 33 | - platform: windows 34 | create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 35 | base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 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 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # youtube_api_example 2 | 3 | Demonstrates how to use the youtube_api_client plugin. 4 | -------------------------------------------------------------------------------- /example/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 | 11 | linter: 12 | # The lint rules applied to this project can be customized in the 13 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 14 | # included above or to enable additional rules. A list of all available lints 15 | # and their documentation is published at 16 | # https://dart-lang.github.io/linter/lints/index.html. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | # Additional information about this file can be found at 27 | # https://dart.dev/guides/language/analysis-options 28 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 37 | applicationId "com.example.example" 38 | minSdkVersion 16 39 | targetSdkVersion 30 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 59 | } 60 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/hyungtae/Documents/development/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/hyungtae/Documents/Github/youtube_api/example" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=lib/main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1.0.0" 10 | export "DART_OBFUSCATION=false" 11 | export "TRACK_WIDGET_CREATION=true" 12 | export "TREE_SHAKE_ICONS=false" 13 | export "PACKAGE_CONFIG=.dart_tool/package_config.json" 14 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.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 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - Flutter (from `Flutter`) 6 | 7 | EXTERNAL SOURCES: 8 | Flutter: 9 | :path: Flutter 10 | 11 | SPEC CHECKSUMS: 12 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 13 | 14 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 15 | 16 | COCOAPODS: 1.10.1 17 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/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. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:youtube_api_client/youtube_api.dart'; 3 | 4 | void main() => runApp(MyApp()); 5 | 6 | class MyApp extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return MaterialApp( 10 | home: DemoApp(), 11 | ); 12 | } 13 | } 14 | 15 | class DemoApp extends StatefulWidget { 16 | @override 17 | _DemoAppState createState() => _DemoAppState(); 18 | } 19 | 20 | class _DemoAppState extends State { 21 | static String _key = "YOUR_API_KEY"; 22 | 23 | final _youtube = YoutubeApi(_key); 24 | List _result = []; 25 | 26 | Future callAPI() async { 27 | String query = "Flutter"; 28 | _result = await _youtube.search( 29 | query, 30 | options: SearchOptions( 31 | order: Order.relevance, 32 | videoDuration: VideoDuration.any, 33 | ), 34 | ); 35 | _result = await _youtube.search( 36 | query, 37 | options: SearchOptions( 38 | type: [ResultType.channel], 39 | ), 40 | ); 41 | _result = (await _youtube.nextPage()) ?? []; 42 | _result = await _youtube.searchVideosById([ 43 | "4AoFA19gbLo" 44 | ], parts: { 45 | VideoPart.snippet, 46 | VideoPart.contentDetails, 47 | }); 48 | _result = await _youtube 49 | .searchPlaylistsById(["PLjxrf2q8roU0WrDTm4tUB430Mja7dQEVP"]); 50 | _result = await _youtube.searchChannelsById([ 51 | "UCwXdFgeE9KYzlDdR7TG9cMw" 52 | ], parts: { 53 | ChannelPart.snippet, 54 | ChannelPart.brandingSettings, 55 | }); 56 | setState(() {}); 57 | } 58 | 59 | @override 60 | void initState() { 61 | super.initState(); 62 | callAPI(); 63 | print('hello'); 64 | } 65 | 66 | @override 67 | Widget build(BuildContext context) { 68 | return Scaffold( 69 | backgroundColor: Colors.blue[100], 70 | appBar: AppBar( 71 | title: Text('Youtube API'), 72 | ), 73 | body: ListView( 74 | children: _result.map(listItem).toList(), 75 | ), 76 | ); 77 | } 78 | 79 | Widget listItem(ApiResult obj) { 80 | return Card( 81 | child: Container( 82 | margin: EdgeInsets.symmetric(vertical: 7.0), 83 | padding: EdgeInsets.all(12.0), 84 | child: Row( 85 | crossAxisAlignment: CrossAxisAlignment.start, 86 | children: [ 87 | if (obj.snippet != null) ...[ 88 | if (obj.snippet!.thumbnails != null) 89 | Padding( 90 | padding: const EdgeInsets.only(right: 20.0), 91 | child: Image.network( 92 | obj.snippet!.thumbnails![ThumbnailResolution.default_] 93 | ?.url ?? 94 | '', 95 | width: 120.0, 96 | ), 97 | ), 98 | ], 99 | Expanded( 100 | child: Column( 101 | crossAxisAlignment: CrossAxisAlignment.start, 102 | children: [ 103 | Text( 104 | obj.url, 105 | softWrap: true, 106 | ), 107 | if (obj is YoutubeVideo) 108 | ..._buildVideoElements(obj) 109 | else if (obj is YoutubeChannel) 110 | ..._buildChannelElements(obj) 111 | else if (obj is YoutubePlaylist) 112 | ..._buildPlaylistElements(obj) 113 | ], 114 | ), 115 | ) 116 | ], 117 | ), 118 | ), 119 | ); 120 | } 121 | 122 | List _buildVideoElements(YoutubeVideo obj) => [ 123 | if (obj.snippet != null) ...[ 124 | Text( 125 | obj.snippet!.title ?? '', 126 | style: TextStyle(fontSize: 18.0), 127 | ), 128 | Padding( 129 | padding: EdgeInsets.symmetric(vertical: 3.0), 130 | child: Text( 131 | obj.snippet!.channelTitle ?? '', 132 | style: TextStyle(fontWeight: FontWeight.bold), 133 | ), 134 | ), 135 | Text("publishedAt: ${obj.snippet!.publishedAt}"), 136 | Text("tags: ${obj.snippet!.tags?.take(3).join(", ")}"), 137 | Text("category: ${obj.snippet!.category?.name}"), 138 | ], 139 | if (obj.contentDetails != null) ...[ 140 | Text("duration: ${obj.contentDetails?.duration}"), 141 | ], 142 | ]; 143 | 144 | List _buildChannelElements(YoutubeChannel obj) => [ 145 | if (obj.snippet != null) ...[ 146 | Text( 147 | obj.snippet!.title ?? '', 148 | style: TextStyle(fontSize: 18.0), 149 | ), 150 | Padding( 151 | padding: EdgeInsets.symmetric(vertical: 3.0), 152 | child: Text( 153 | obj.snippet!.customUrl ?? '', 154 | style: TextStyle(fontWeight: FontWeight.bold), 155 | ), 156 | ), 157 | Text("publishedAt: ${obj.snippet!.publishedAt}"), 158 | Text("description: ${obj.snippet!.description}"), 159 | ], 160 | if (obj.brandingSettings != null) ...[ 161 | Text( 162 | obj.brandingSettings!.country ?? '', 163 | style: TextStyle(fontSize: 18.0), 164 | ), 165 | Padding( 166 | padding: EdgeInsets.symmetric(vertical: 3.0), 167 | child: Text( 168 | obj.brandingSettings!.keywords.toString(), 169 | style: TextStyle(fontWeight: FontWeight.bold), 170 | ), 171 | ), 172 | Text("moderateComments: ${obj.brandingSettings!.moderateComments}"), 173 | Text( 174 | "unsubscribedTrailer: ${obj.brandingSettings!.unsubscribedTrailer}"), 175 | ], 176 | ]; 177 | List _buildPlaylistElements(YoutubePlaylist obj) => [ 178 | if (obj.snippet != null) ...[ 179 | Text( 180 | obj.snippet!.title ?? '', 181 | style: TextStyle(fontSize: 18.0), 182 | ), 183 | Padding( 184 | padding: EdgeInsets.symmetric(vertical: 3.0), 185 | child: Text( 186 | obj.snippet!.channelTitle ?? '', 187 | style: TextStyle(fontWeight: FontWeight.bold), 188 | ), 189 | ), 190 | Text("publishedAt: ${obj.snippet!.publishedAt}"), 191 | ], 192 | ]; 193 | } 194 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.example") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Fully re-copy the assets directory on each build to avoid having stale files 127 | # from a previous install. 128 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 129 | install(CODE " 130 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 131 | " COMPONENT Runtime) 132 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 133 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 134 | 135 | # Install the AOT library on non-Debug builds only. 136 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 137 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 138 | COMPONENT Runtime) 139 | endif() 140 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /example/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 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - FlutterMacOS (from `Flutter/ephemeral`) 6 | 7 | EXTERNAL SOURCES: 8 | FlutterMacOS: 9 | :path: Flutter/ephemeral 10 | 11 | SPEC CHECKSUMS: 12 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 13 | 14 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 15 | 16 | COCOAPODS: 1.12.1 17 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/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 = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 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 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: youtube_api_example 2 | description: Demonstrates how to use the youtube_api_client plugin. 3 | version: 1.0.0 4 | 5 | # The following line prevents the package from being accidentally published to 6 | # pub.dev using `pub publish`. This is preferred for private packages. 7 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 8 | 9 | environment: 10 | sdk: ">=2.17.0 <3.0.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | youtube_api_client: 17 | # When depending on this package from a real application you should use: 18 | # youtube_api_client: ^x.y.z 19 | # See https://dart.dev/tools/pub/dependencies#version-constraints 20 | # The example app is bundled with the plugin so we use a path dependency on 21 | # the parent directory to use the current plugin's version. 22 | path: ../ 23 | 24 | # The following adds the Cupertino Icons font to your application. 25 | # Use with the CupertinoIcons class for iOS style icons. 26 | cupertino_icons: ^1.0.2 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | 32 | # For information on the generic Dart part of this file, see the 33 | # following page: https://dart.dev/tools/pub/pubspec 34 | 35 | # The following section is specific to Flutter. 36 | flutter: 37 | # The following line ensures that the Material Icons font is 38 | # included with your application, so that you can use the icons in 39 | # the material Icons class. 40 | uses-material-design: true 41 | 42 | # To add assets to your application, add an assets section, like this: 43 | # assets: 44 | # - images/a_dot_burr.jpeg 45 | # - images/a_dot_ham.jpeg 46 | 47 | # An image asset can refer to one or more resolution-specific "variants", see 48 | # https://flutter.dev/assets-and-images/#resolution-aware. 49 | 50 | # For details regarding adding assets from package dependencies, see 51 | # https://flutter.dev/assets-and-images/#from-packages 52 | 53 | # To add custom fonts to your application, add a fonts section here, 54 | # in this "flutter" section. Each entry in this list should have a 55 | # "family" key with the font family name, and a "fonts" key with a 56 | # list giving the asset and other descriptors for the font. For 57 | # example: 58 | # fonts: 59 | # - family: Schyler 60 | # fonts: 61 | # - asset: fonts/Schyler-Regular.ttf 62 | # - asset: fonts/Schyler-Italic.ttf 63 | # style: italic 64 | # - family: Trajan Pro 65 | # fonts: 66 | # - asset: fonts/TrajanPro.ttf 67 | # - asset: fonts/TrajanPro_Bold.ttf 68 | # weight: 700 69 | # 70 | # For details regarding fonts from package dependencies, 71 | # see https://flutter.dev/custom-fonts/#from-packages 72 | -------------------------------------------------------------------------------- /example/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 that Flutter provides. 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:youtube_api_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => widget is Text && 22 | widget.data!.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | example 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 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 | } 24 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(SET CMP0063 NEW) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Fully re-copy the assets directory on each build to avoid having stale files 91 | # from a previous install. 92 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 93 | install(CODE " 94 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 95 | " COMPONENT Runtime) 96 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 97 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 98 | 99 | # Install the AOT library on non-Debug builds only. 100 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 101 | CONFIGURATIONS Profile;Release 102 | COMPONENT Runtime) 103 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # === Flutter Library === 14 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 15 | 16 | # Published to parent scope for install step. 17 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 18 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 19 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 20 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 21 | 22 | list(APPEND FLUTTER_LIBRARY_HEADERS 23 | "flutter_export.h" 24 | "flutter_windows.h" 25 | "flutter_messenger.h" 26 | "flutter_plugin_registrar.h" 27 | "flutter_texture_registrar.h" 28 | ) 29 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 30 | add_library(flutter INTERFACE) 31 | target_include_directories(flutter INTERFACE 32 | "${EPHEMERAL_DIR}" 33 | ) 34 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 35 | add_dependencies(flutter flutter_assemble) 36 | 37 | # === Wrapper === 38 | list(APPEND CPP_WRAPPER_SOURCES_CORE 39 | "core_implementations.cc" 40 | "standard_codec.cc" 41 | ) 42 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 43 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 44 | "plugin_registrar.cc" 45 | ) 46 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 47 | list(APPEND CPP_WRAPPER_SOURCES_APP 48 | "flutter_engine.cc" 49 | "flutter_view_controller.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 52 | 53 | # Wrapper sources needed for a plugin. 54 | add_library(flutter_wrapper_plugin STATIC 55 | ${CPP_WRAPPER_SOURCES_CORE} 56 | ${CPP_WRAPPER_SOURCES_PLUGIN} 57 | ) 58 | apply_standard_settings(flutter_wrapper_plugin) 59 | set_target_properties(flutter_wrapper_plugin PROPERTIES 60 | POSITION_INDEPENDENT_CODE ON) 61 | set_target_properties(flutter_wrapper_plugin PROPERTIES 62 | CXX_VISIBILITY_PRESET hidden) 63 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 64 | target_include_directories(flutter_wrapper_plugin PUBLIC 65 | "${WRAPPER_ROOT}/include" 66 | ) 67 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 68 | 69 | # Wrapper sources needed for the runner. 70 | add_library(flutter_wrapper_app STATIC 71 | ${CPP_WRAPPER_SOURCES_CORE} 72 | ${CPP_WRAPPER_SOURCES_APP} 73 | ) 74 | apply_standard_settings(flutter_wrapper_app) 75 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 76 | target_include_directories(flutter_wrapper_app PUBLIC 77 | "${WRAPPER_ROOT}/include" 78 | ) 79 | add_dependencies(flutter_wrapper_app flutter_assemble) 80 | 81 | # === Flutter tool backend === 82 | # _phony_ is a non-existent file to force this command to run every time, 83 | # since currently there's no way to get a full input/output list from the 84 | # flutter tool. 85 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 86 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 87 | add_custom_command( 88 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 89 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 90 | ${CPP_WRAPPER_SOURCES_APP} 91 | ${PHONY_OUTPUT} 92 | COMMAND ${CMAKE_COMMAND} -E env 93 | ${FLUTTER_TOOL_ENVIRONMENT} 94 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 95 | windows-x64 $ 96 | VERBATIM 97 | ) 98 | add_custom_target(flutter_assemble DEPENDS 99 | "${FLUTTER_LIBRARY}" 100 | ${FLUTTER_LIBRARY_HEADERS} 101 | ${CPP_WRAPPER_SOURCES_CORE} 102 | ${CPP_WRAPPER_SOURCES_PLUGIN} 103 | ${CPP_WRAPPER_SOURCES_APP} 104 | ) 105 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | return true; 35 | } 36 | 37 | void FlutterWindow::OnDestroy() { 38 | if (flutter_controller_) { 39 | flutter_controller_ = nullptr; 40 | } 41 | 42 | Win32Window::OnDestroy(); 43 | } 44 | 45 | LRESULT 46 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 47 | WPARAM const wparam, 48 | LPARAM const lparam) noexcept { 49 | // Give Flutter, including plugins, an opportunity to handle window messages. 50 | if (flutter_controller_) { 51 | std::optional result = 52 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 53 | lparam); 54 | if (result) { 55 | return *result; 56 | } 57 | } 58 | 59 | switch (message) { 60 | case WM_FONTCHANGE: 61 | flutter_controller_->engine()->ReloadSystemFonts(); 62 | break; 63 | } 64 | 65 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 66 | } 67 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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"example", 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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource.h" 7 | 8 | namespace { 9 | 10 | /// Window attribute that enables dark mode window decorations. 11 | /// 12 | /// Redefined in case the developer's machine has a Windows SDK older than 13 | /// version 10.0.22000.0. 14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE 16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 17 | #endif 18 | 19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 20 | 21 | /// Registry key for app theme preference. 22 | /// 23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing 24 | /// value indicates apps should use light mode. 25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] = 26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; 27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; 28 | 29 | // The number of Win32Window objects that currently exist. 30 | static int g_active_window_count = 0; 31 | 32 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 33 | 34 | // Scale helper to convert logical scaler values to physical using passed in 35 | // scale factor 36 | int Scale(int source, double scale_factor) { 37 | return static_cast(source * scale_factor); 38 | } 39 | 40 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 41 | // This API is only needed for PerMonitor V1 awareness mode. 42 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 43 | HMODULE user32_module = LoadLibraryA("User32.dll"); 44 | if (!user32_module) { 45 | return; 46 | } 47 | auto enable_non_client_dpi_scaling = 48 | reinterpret_cast( 49 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 50 | if (enable_non_client_dpi_scaling != nullptr) { 51 | enable_non_client_dpi_scaling(hwnd); 52 | } 53 | FreeLibrary(user32_module); 54 | } 55 | 56 | } // namespace 57 | 58 | // Manages the Win32Window's window class registration. 59 | class WindowClassRegistrar { 60 | public: 61 | ~WindowClassRegistrar() = default; 62 | 63 | // Returns the singleton registrar instance. 64 | static WindowClassRegistrar* GetInstance() { 65 | if (!instance_) { 66 | instance_ = new WindowClassRegistrar(); 67 | } 68 | return instance_; 69 | } 70 | 71 | // Returns the name of the window class, registering the class if it hasn't 72 | // previously been registered. 73 | const wchar_t* GetWindowClass(); 74 | 75 | // Unregisters the window class. Should only be called if there are no 76 | // instances of the window. 77 | void UnregisterWindowClass(); 78 | 79 | private: 80 | WindowClassRegistrar() = default; 81 | 82 | static WindowClassRegistrar* instance_; 83 | 84 | bool class_registered_ = false; 85 | }; 86 | 87 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 88 | 89 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 90 | if (!class_registered_) { 91 | WNDCLASS window_class{}; 92 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 93 | window_class.lpszClassName = kWindowClassName; 94 | window_class.style = CS_HREDRAW | CS_VREDRAW; 95 | window_class.cbClsExtra = 0; 96 | window_class.cbWndExtra = 0; 97 | window_class.hInstance = GetModuleHandle(nullptr); 98 | window_class.hIcon = 99 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 100 | window_class.hbrBackground = 0; 101 | window_class.lpszMenuName = nullptr; 102 | window_class.lpfnWndProc = Win32Window::WndProc; 103 | RegisterClass(&window_class); 104 | class_registered_ = true; 105 | } 106 | return kWindowClassName; 107 | } 108 | 109 | void WindowClassRegistrar::UnregisterWindowClass() { 110 | UnregisterClass(kWindowClassName, nullptr); 111 | class_registered_ = false; 112 | } 113 | 114 | Win32Window::Win32Window() { 115 | ++g_active_window_count; 116 | } 117 | 118 | Win32Window::~Win32Window() { 119 | --g_active_window_count; 120 | Destroy(); 121 | } 122 | 123 | bool Win32Window::Create(const std::wstring& title, 124 | const Point& origin, 125 | const Size& size) { 126 | Destroy(); 127 | 128 | const wchar_t* window_class = 129 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 130 | 131 | const POINT target_point = {static_cast(origin.x), 132 | static_cast(origin.y)}; 133 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 134 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 135 | double scale_factor = dpi / 96.0; 136 | 137 | HWND window = CreateWindow( 138 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW, 139 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 140 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 141 | nullptr, nullptr, GetModuleHandle(nullptr), this); 142 | 143 | if (!window) { 144 | return false; 145 | } 146 | 147 | UpdateTheme(window); 148 | 149 | return OnCreate(); 150 | } 151 | 152 | bool Win32Window::Show() { 153 | return ShowWindow(window_handle_, SW_SHOWNORMAL); 154 | } 155 | 156 | // static 157 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 158 | UINT const message, 159 | WPARAM const wparam, 160 | LPARAM const lparam) noexcept { 161 | if (message == WM_NCCREATE) { 162 | auto window_struct = reinterpret_cast(lparam); 163 | SetWindowLongPtr(window, GWLP_USERDATA, 164 | reinterpret_cast(window_struct->lpCreateParams)); 165 | 166 | auto that = static_cast(window_struct->lpCreateParams); 167 | EnableFullDpiSupportIfAvailable(window); 168 | that->window_handle_ = window; 169 | } else if (Win32Window* that = GetThisFromHandle(window)) { 170 | return that->MessageHandler(window, message, wparam, lparam); 171 | } 172 | 173 | return DefWindowProc(window, message, wparam, lparam); 174 | } 175 | 176 | LRESULT 177 | Win32Window::MessageHandler(HWND hwnd, 178 | UINT const message, 179 | WPARAM const wparam, 180 | LPARAM const lparam) noexcept { 181 | switch (message) { 182 | case WM_DESTROY: 183 | window_handle_ = nullptr; 184 | Destroy(); 185 | if (quit_on_close_) { 186 | PostQuitMessage(0); 187 | } 188 | return 0; 189 | 190 | case WM_DPICHANGED: { 191 | auto newRectSize = reinterpret_cast(lparam); 192 | LONG newWidth = newRectSize->right - newRectSize->left; 193 | LONG newHeight = newRectSize->bottom - newRectSize->top; 194 | 195 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 196 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 197 | 198 | return 0; 199 | } 200 | case WM_SIZE: { 201 | RECT rect = GetClientArea(); 202 | if (child_content_ != nullptr) { 203 | // Size and position the child window. 204 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 205 | rect.bottom - rect.top, TRUE); 206 | } 207 | return 0; 208 | } 209 | 210 | case WM_ACTIVATE: 211 | if (child_content_ != nullptr) { 212 | SetFocus(child_content_); 213 | } 214 | return 0; 215 | 216 | case WM_DWMCOLORIZATIONCOLORCHANGED: 217 | UpdateTheme(hwnd); 218 | return 0; 219 | } 220 | 221 | return DefWindowProc(window_handle_, message, wparam, lparam); 222 | } 223 | 224 | void Win32Window::Destroy() { 225 | OnDestroy(); 226 | 227 | if (window_handle_) { 228 | DestroyWindow(window_handle_); 229 | window_handle_ = nullptr; 230 | } 231 | if (g_active_window_count == 0) { 232 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 233 | } 234 | } 235 | 236 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 237 | return reinterpret_cast( 238 | GetWindowLongPtr(window, GWLP_USERDATA)); 239 | } 240 | 241 | void Win32Window::SetChildContent(HWND content) { 242 | child_content_ = content; 243 | SetParent(content, window_handle_); 244 | RECT frame = GetClientArea(); 245 | 246 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 247 | frame.bottom - frame.top, true); 248 | 249 | SetFocus(child_content_); 250 | } 251 | 252 | RECT Win32Window::GetClientArea() { 253 | RECT frame; 254 | GetClientRect(window_handle_, &frame); 255 | return frame; 256 | } 257 | 258 | HWND Win32Window::GetHandle() { 259 | return window_handle_; 260 | } 261 | 262 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 263 | quit_on_close_ = quit_on_close; 264 | } 265 | 266 | bool Win32Window::OnCreate() { 267 | // No-op; provided for subclasses. 268 | return true; 269 | } 270 | 271 | void Win32Window::OnDestroy() { 272 | // No-op; provided for subclasses. 273 | } 274 | 275 | void Win32Window::UpdateTheme(HWND const window) { 276 | DWORD light_mode; 277 | DWORD light_mode_size = sizeof(light_mode); 278 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, 279 | kGetPreferredBrightnessRegValue, 280 | RRF_RT_REG_DWORD, nullptr, &light_mode, 281 | &light_mode_size); 282 | 283 | if (result == ERROR_SUCCESS) { 284 | BOOL enable_dark_mode = light_mode == 0; 285 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, 286 | &enable_dark_mode, sizeof(enable_dark_mode)); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /lib/channel.dart: -------------------------------------------------------------------------------- 1 | export 'package:youtube_api_client/model/channel/channel_snippet.dart'; 2 | export 'package:youtube_api_client/model/channel/channel_options.dart'; 3 | export 'package:youtube_api_client/model/channel/youtube_channel.dart'; 4 | export 'package:youtube_api_client/enum/channel/channel_part.dart'; 5 | export 'package:youtube_api_client/model/channel/branding_settings.dart'; 6 | -------------------------------------------------------------------------------- /lib/enum/category.dart: -------------------------------------------------------------------------------- 1 | // Based on this list: https://gist.github.com/dgp/1b24bf2961521bd75d6c 2 | import 'package:collection/collection.dart'; 3 | 4 | enum Category { 5 | autosAndVehicles(2), 6 | filmAndAnimation(1), 7 | music(10), 8 | petsAndAnimals(15), 9 | sports(17), 10 | shortMovies(18), 11 | travelAndEvents(19), 12 | gaming(20), 13 | videoblogging(21), 14 | peopleAndBlogs(22), 15 | comedy(23), 16 | entertainment(24), 17 | newsAndPolitics(25), 18 | howToAndStyle(26), 19 | education(27), 20 | scienceAndTechnology(28), 21 | nonprofitsAndActivism(29), 22 | movies(30), 23 | animeAndAnimation(31), 24 | actionAndAdventure(32), 25 | classics(33), 26 | comedy2(34), 27 | documentary(35), 28 | drama(36), 29 | family(37), 30 | foreign(38), 31 | horror(39), 32 | scifiAndFantasy(40), 33 | thriller(41), 34 | shorts(42), 35 | shows(43), 36 | trailers(44); 37 | 38 | const Category(this.categoryId); 39 | 40 | final int categoryId; 41 | 42 | static Category? fromCategoryId(int id) => 43 | Category.values.firstWhereOrNull((e) => e.categoryId == id); 44 | } 45 | -------------------------------------------------------------------------------- /lib/enum/channel/channel_part.dart: -------------------------------------------------------------------------------- 1 | /// The `part` parameter specifies a comma-separated list of one or more 2 | /// `channel` resource properties that the API response will include. 3 | /// 4 | /// `string` 5 | /// 6 | /// If the parameter identifies a property that contains child properties, the 7 | /// child properties will be included in the response. For example, in a 8 | /// `channel` resource, the `contentDetails` property contains other properties, 9 | /// such as the `uploads` properties. As such, if you set `part=contentDetails`, 10 | /// the API response will also contain all of those nested properties. 11 | /// 12 | /// The following list contains the `part` names that you can include in the 13 | /// parameter value: 14 | /// 15 | /// * `auditDetails` 16 | /// * `brandingSettings` 17 | /// * `contentDetails` 18 | /// * `contentOwnerDetails` 19 | /// * `id` 20 | /// * `localizations` 21 | /// * `snippet` 22 | /// * `statistics` 23 | /// * `status` 24 | /// * `topicDetails` 25 | enum ChannelPart { 26 | auditDetails, 27 | brandingSettings, 28 | contentDetails, 29 | contentOwnerDetails, 30 | id, 31 | localizations, 32 | snippet, 33 | statistics, 34 | status, 35 | topicDetails; 36 | 37 | static const implementedParts = {snippet, brandingSettings}; 38 | } 39 | -------------------------------------------------------------------------------- /lib/enum/playlist/playlist_part.dart: -------------------------------------------------------------------------------- 1 | /// The `part` parameter specifies a comma-separated list of one or more 2 | /// `playlist` resource properties that the API response will include. 3 | /// 4 | /// `string` 5 | /// 6 | /// If the parameter identifies a property that contains child properties, the 7 | /// child properties will be included in the response. For example, in a 8 | /// `playlist` resource, the `snippet` property contains properties like 9 | /// `author`, `title`, `description`, and `timeCreated`. As such, if you set 10 | /// `part=snippet`, the API response will contain all of those properties. 11 | /// 12 | /// The following list contains the `part` names that you can include in the 13 | /// parameter value: 14 | /// 15 | /// * `contentDetails` 16 | /// * `id` 17 | /// * `localizations` 18 | /// * `player` 19 | /// * `snippet` 20 | /// * `status` 21 | enum PlaylistPart { 22 | contentDetails, 23 | id, 24 | localizations, 25 | player, 26 | snippet, 27 | status; 28 | 29 | static const implementedParts = {snippet}; 30 | } 31 | -------------------------------------------------------------------------------- /lib/enum/query_parameter.dart: -------------------------------------------------------------------------------- 1 | /// Supported keys for the query options. 2 | enum QueryParameter { 3 | part, 4 | channelId, 5 | channelType, 6 | eventType, 7 | location, 8 | locationRadius, 9 | maxResults, 10 | onBehalfOfContentOwner, 11 | order, 12 | pageToken, 13 | publishedAfter, 14 | publishedBefore, 15 | q, 16 | regionCode, 17 | relevanceLanguage, 18 | safeSearch, 19 | topicId, 20 | type, 21 | videoCaption, 22 | videoCategoryId, 23 | videoDefinition, 24 | videoDimension, 25 | videoDuration, 26 | videoEmbeddable, 27 | videoLicense, 28 | videoSyndicated, 29 | videoType, 30 | forContentOwner, 31 | forDeveloper, 32 | forMine, 33 | chart, 34 | id, 35 | myRating, 36 | hl, 37 | maxHeight, 38 | maxWidth, 39 | forUsername, 40 | managedByMe, 41 | mine, 42 | } 43 | -------------------------------------------------------------------------------- /lib/enum/result_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/model/channel/youtube_channel.dart'; 2 | import 'package:youtube_api_client/model/playlist/youtube_playlist.dart'; 3 | import 'package:youtube_api_client/model/video/youtube_video.dart'; 4 | import 'package:youtube_api_client/model/youtube_api_result.dart'; 5 | 6 | /// The `type` parameter restricts a search query to only retrieve a 7 | /// particular type of resource. The value is a comma-separated list of 8 | /// resource types. The default value is `video,channel,playlist`. 9 | /// 10 | /// `string` 11 | /// 12 | /// Acceptable values are: 13 | /// 14 | /// * `channel` 15 | /// * `playlist` 16 | /// * `video` 17 | enum ResultType { 18 | video, 19 | channel, 20 | playlist; 21 | 22 | static ResultType fromType() => switch (T) { 23 | YoutubeVideo => video, 24 | YoutubeChannel => channel, 25 | YoutubePlaylist => playlist, 26 | _ => throw Exception('Invalid type.'), 27 | }; 28 | 29 | String get unencodedPath { 30 | const base = 'youtube/v3/'; 31 | return switch (this) { 32 | video => '${base}videos', 33 | channel => '${base}channels', 34 | playlist => '${base}playlists', 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/enum/search/channel_type.dart: -------------------------------------------------------------------------------- 1 | /// The `channelType` parameter lets you restrict a search to a particular 2 | /// type of channel. 3 | /// 4 | /// `string` 5 | /// 6 | /// Acceptable values are: 7 | /// * `any` – Return all channels. 8 | /// * `show` – Only retrieve shows. 9 | enum ChannelType { any, show } 10 | -------------------------------------------------------------------------------- /lib/enum/search/event_type.dart: -------------------------------------------------------------------------------- 1 | /// The `eventType` parameter restricts a search to broadcast events. If 2 | /// you specify a value for this parameter, you must also set the 3 | /// `type` parameter's value to `video`. 4 | /// 5 | /// `string` 6 | /// 7 | /// Acceptable values are: 8 | /// 9 | /// * `completed` – Only include completed broadcasts. 10 | /// * `live` – Only include active broadcasts. 11 | /// * `upcoming` – Only include upcoming broadcasts. 12 | enum EventType { completed, live, upcoming } 13 | -------------------------------------------------------------------------------- /lib/enum/search/order.dart: -------------------------------------------------------------------------------- 1 | /// The `order` parameter specifies the method that will be used to order 2 | /// resources in the API response. The default value is `relevance`. 3 | /// 4 | /// `string` 5 | /// 6 | /// Acceptable values are: 7 | /// 8 | /// * `date` – Resources are sorted in reverse chronological order based 9 | /// on the date they were created. 10 | /// * `rating` – Resources are sorted from highest to lowest rating. 11 | /// * `relevance` – Resources are sorted based on their relevance to the 12 | /// search query. This is the default value for this parameter. 13 | /// * `title` – Resources are sorted alphabetically by title. 14 | /// * `videoCount` – Channels are sorted in descending order of their 15 | /// number of uploaded videos. 16 | /// * `viewCount` – Resources are sorted from highest to lowest number of 17 | /// views. For live broadcasts, videos are sorted by number of concurrent 18 | /// viewers while the broadcasts are ongoing. 19 | enum Order { date, rating, relevance, title, videoCount, viewCount } 20 | -------------------------------------------------------------------------------- /lib/enum/search/safe_search.dart: -------------------------------------------------------------------------------- 1 | /// The `safeSearch` parameter indicates whether the search results should 2 | /// include restricted content as well as standard content. 3 | /// 4 | /// `string` 5 | /// 6 | /// Acceptable values are: 7 | /// 8 | /// * `moderate` – YouTube will filter some content from search results and, 9 | /// at the least, will filter content that is restricted in your locale. 10 | /// Based on their content, search results could be removed from search 11 | /// results or demoted in search results. This is the default parameter 12 | /// value. 13 | /// * `none` – YouTube will not filter the search result set. 14 | /// * `strict` – YouTube will try to exclude all restricted content from the 15 | /// search result set. Based on their content, search results could be 16 | /// removed from search results or demoted in search results. 17 | enum SafeSearch { moderate, none, strict } 18 | -------------------------------------------------------------------------------- /lib/enum/search/search_part.dart: -------------------------------------------------------------------------------- 1 | /// The `part` parameter specifies a comma-separated list of one or more 2 | /// `search` resource properties that the API response will include. Set the 3 | /// parameter value to `snippet`. 4 | /// 5 | /// `string` 6 | enum SearchPart { snippet } 7 | -------------------------------------------------------------------------------- /lib/enum/search/topic.dart: -------------------------------------------------------------------------------- 1 | enum Topic { 2 | // **Music topics** 3 | /// Music (parent topic) 4 | music('/m/04rlf', parent: true), 5 | 6 | /// Christian music 7 | cristianMusic('/m/02mscn'), 8 | 9 | /// Classical music 10 | classicalMusic('/m/0ggq0m'), 11 | 12 | /// Country 13 | country('/m/01lyv'), 14 | 15 | /// Electronic music 16 | electronicMusic('/m/02lkt'), 17 | 18 | /// Hip hop music 19 | hipHopMusic('/m/0glt670'), 20 | 21 | /// Independent music 22 | independentMusic('/m/05rwpb'), 23 | 24 | /// Jazz 25 | jazz(r'/m/03\_d0'), 26 | 27 | /// Music of Asia 28 | musicOfAsia('/m/028sqc'), 29 | 30 | /// Music of Latin America 31 | musicOfLatinAmerica('/m/0g293'), 32 | 33 | /// Pop music 34 | popMusic('/m/064t9'), 35 | 36 | /// Reggae 37 | reggae('/m/06cqb'), 38 | 39 | /// Rhythm and blues 40 | rhythmAndBlues('/m/06j6l'), 41 | 42 | /// Rock music 43 | rockMusic('/m/06by7'), 44 | 45 | /// Soul music 46 | soulMusic('/m/0gywn'), 47 | // **Gaming topics** 48 | 49 | /// Gaming (parent topic) 50 | gaming('/m/0bzvm2', parent: true), 51 | 52 | /// Action game 53 | actionGame('/m/025zzc'), 54 | 55 | /// Action-adventure game 56 | actionAdventureGame('/m/02ntfj'), 57 | 58 | /// Casual game 59 | casualGame('/m/0b1vjn'), 60 | 61 | /// Music video game 62 | musicVideoGame('/m/02hygl'), 63 | 64 | /// Puzzle video game 65 | puzzleVideoGame('/m/04q1x3q'), 66 | 67 | /// Racing video game 68 | racingVideoGame('/m/01sjng'), 69 | 70 | /// Role-playing video game 71 | rolePlayingVideoGame('/m/0403l3g'), 72 | 73 | /// Simulation video game 74 | simulationVideoGame('/m/021bp2'), 75 | 76 | /// Sports game 77 | sportsGame('/m/022dc6'), 78 | 79 | /// Strategy video game 80 | strategyVideoGame(r'/m/03hf\_rm'), 81 | // **Sports topics** 82 | 83 | /// Sports (parent topic) 84 | sports('/m/06ntj', parent: true), 85 | 86 | /// American football 87 | americanFootball(r'/m/0jm\_'), 88 | 89 | /// Baseball 90 | baseball('/m/018jz'), 91 | 92 | /// Basketball 93 | basketball('/m/018w8'), 94 | 95 | /// Boxing 96 | boxing('/m/01cgz'), 97 | 98 | /// Cricket 99 | cricket(r'/m/09xp\_'), 100 | 101 | /// Football 102 | football('/m/02vx4'), 103 | 104 | /// Golf 105 | golf('/m/037hz'), 106 | 107 | /// Ice hockey 108 | iceHockey('/m/03tmr'), 109 | 110 | /// Mixed martial arts 111 | mixedMartialArts('/m/01h7lh'), 112 | 113 | /// Motorsport 114 | motorsport('/m/0410tth'), 115 | 116 | /// Tennis 117 | tennis('/m/07bs0'), 118 | 119 | /// Volleyball 120 | volleyball(r'/m/07\_53'), 121 | // **Entertainment topics** 122 | 123 | /// Entertainment (parent topic) 124 | entertainment('/m/02jjt', parent: true), 125 | 126 | /// Humor 127 | humor('/m/09kqc'), 128 | 129 | /// Movies 130 | movies('/m/02vxn'), 131 | 132 | /// Performing arts 133 | performingArts('/m/05qjc'), 134 | 135 | /// Professional wrestling 136 | professionalWrestling('/m/066wd'), 137 | 138 | /// TV shows 139 | tvShows('/m/0f2f9'), 140 | 141 | // **Lifestyle topics** 142 | 143 | /// Lifestyle (parent topic) 144 | lifestyle(r'/m/019\_rr', parent: true), 145 | 146 | /// Fashion 147 | fashion('/m/032tl'), 148 | 149 | /// Fitness 150 | fitness('/m/027x7n'), 151 | 152 | /// Food 153 | food('/m/02wbm'), 154 | 155 | /// Hobby 156 | hobby('/m/03glg'), 157 | 158 | /// Pets 159 | pets('/m/068hy'), 160 | 161 | /// Physical attractiveness \[Beauty\] 162 | beauty('/m/041xxh'), 163 | 164 | /// Technology 165 | technology('/m/07c1v'), 166 | 167 | /// Tourism 168 | tourism('/m/07bxq'), 169 | 170 | /// Vehicles 171 | vehicles('/m/07yv9'), 172 | // **Society topics** 173 | 174 | /// Society (parent topic) 175 | society('/m/098wr', parent: true), 176 | 177 | /// Business 178 | business('/m/09s1f'), 179 | 180 | /// Health 181 | health('/m/0kt51'), 182 | 183 | /// Military 184 | military('/m/01h6rj'), 185 | 186 | /// Politics 187 | politics('/m/05qt0'), 188 | 189 | /// Religion 190 | religion('/m/06bvp'), 191 | 192 | // **Other topics** 193 | 194 | /// Knowledge 195 | knowledge('/m/01k8wb'), 196 | ; 197 | 198 | const Topic(this.id, {this.parent = false}); 199 | final String id; 200 | final bool parent; 201 | 202 | Set? get children => switch (this) { 203 | music => { 204 | cristianMusic, 205 | classicalMusic, 206 | country, 207 | electronicMusic, 208 | hipHopMusic, 209 | independentMusic, 210 | jazz, 211 | musicOfAsia, 212 | musicOfLatinAmerica, 213 | popMusic, 214 | reggae, 215 | rhythmAndBlues, 216 | rockMusic, 217 | soulMusic, 218 | }, 219 | gaming => { 220 | actionGame, 221 | actionAdventureGame, 222 | casualGame, 223 | musicVideoGame, 224 | puzzleVideoGame, 225 | racingVideoGame, 226 | rolePlayingVideoGame, 227 | simulationVideoGame, 228 | sportsGame, 229 | strategyVideoGame, 230 | }, 231 | sports => { 232 | americanFootball, 233 | baseball, 234 | basketball, 235 | boxing, 236 | cricket, 237 | football, 238 | golf, 239 | iceHockey, 240 | mixedMartialArts, 241 | motorsport, 242 | tennis, 243 | volleyball, 244 | }, 245 | entertainment => { 246 | humor, 247 | movies, 248 | performingArts, 249 | professionalWrestling, 250 | tvShows, 251 | }, 252 | lifestyle => { 253 | fashion, 254 | fitness, 255 | food, 256 | hobby, 257 | pets, 258 | beauty, 259 | technology, 260 | tourism, 261 | vehicles, 262 | }, 263 | society => { 264 | business, 265 | health, 266 | military, 267 | politics, 268 | religion, 269 | }, 270 | _ => null, 271 | }; 272 | } 273 | -------------------------------------------------------------------------------- /lib/enum/video/chart.dart: -------------------------------------------------------------------------------- 1 | /// The `chart` parameter identifies the chart that you want to retrieve. 2 | /// 3 | /// `string` 4 | /// 5 | /// Acceptable values are: 6 | /// 7 | /// * `mostPopular` – Return the most popular videos for the specified content 8 | /// region and video category. 9 | enum Chart { mostPopular } 10 | -------------------------------------------------------------------------------- /lib/enum/video/live_broadcast_content.dart: -------------------------------------------------------------------------------- 1 | enum LiveBroadcastContent { live, none, upcoming } 2 | -------------------------------------------------------------------------------- /lib/enum/video/my_rating.dart: -------------------------------------------------------------------------------- 1 | /// This parameter can only be used in a properly authorized request. Set this 2 | /// parameter's value to `like` or `dislike` to instruct the API to only return 3 | /// videos liked or disliked by the authenticated user. 4 | /// 5 | /// `string` 6 | /// 7 | /// Acceptable values are: 8 | /// 9 | /// * `dislike` – Returns only videos disliked by the authenticated user. 10 | /// * `like` – Returns only video liked by the authenticated user. 11 | enum MyRating { dislike, like } 12 | -------------------------------------------------------------------------------- /lib/enum/video/video_part.dart: -------------------------------------------------------------------------------- 1 | /// The `part` parameter specifies a comma-separated list of one or more 2 | /// `video` resource properties that the API response will include. 3 | /// 4 | /// `string` 5 | /// 6 | /// If the parameter identifies a property that contains child properties, the 7 | /// child properties will be included in the response. For example, in a `video` 8 | /// resource, the `snippet` property contains the `channelId`, `title`, 9 | /// `description`, `tags`, and `categoryId` properties. As such, if you set 10 | /// `part=snippet`, the API response will contain all of those properties. 11 | /// 12 | /// The following list contains the `part` names that you can include in the 13 | /// parameter value: 14 | /// 15 | /// * `contentDetails` 16 | /// * `fileDetails` 17 | /// * `id` 18 | /// * `liveStreamingDetails` 19 | /// * `localizations` 20 | /// * `player` 21 | /// * `processingDetails` 22 | /// * `recordingDetails` 23 | /// * `snippet` 24 | /// * `statistics` 25 | /// * `status` 26 | /// * `suggestions` 27 | /// * `topicDetails` 28 | enum VideoPart { 29 | contentDetails, 30 | fileDetails, 31 | id, 32 | liveStreamingDetails, 33 | localizations, 34 | player, 35 | processingDetails, 36 | recordingDetails, 37 | snippet, 38 | statistics, 39 | status, 40 | suggestions, 41 | topicDetails; 42 | 43 | static const implementedParts = {snippet, contentDetails}; 44 | } 45 | -------------------------------------------------------------------------------- /lib/enum/video/video_projection.dart: -------------------------------------------------------------------------------- 1 | enum VideoProjection { 2 | rectangular, 3 | threeSixty; 4 | 5 | static VideoProjection? fromString(String string) => switch (string) { 6 | "rectangular" => rectangular, 7 | "360" => threeSixty, 8 | _ => null, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /lib/enum/video_caption.dart: -------------------------------------------------------------------------------- 1 | /// The `videoCaption` parameter indicates whether the API should filter 2 | /// video search results based on whether they have captions. If you specify a 3 | /// value for this parameter, you must also set the `[type](#type)` parameter's 4 | /// value to `video`. 5 | /// 6 | /// `string` 7 | /// 8 | /// Acceptable values are: 9 | /// 10 | /// * `any` – Do not filter results based on caption availability. 11 | /// * `closedCaption` – Only include videos that have captions. 12 | /// * `none` – Only include videos that do not have captions. 13 | enum VideoCaption { 14 | any, 15 | closedCaption, 16 | none; 17 | 18 | static VideoCaption? fromString(String string) => switch (string) { 19 | "false" => none, 20 | "true" => closedCaption, 21 | _ => null, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /lib/enum/video_definition.dart: -------------------------------------------------------------------------------- 1 | /// The `videoDefinition` parameter lets you restrict a search to only 2 | /// include either high definition (HD) or standard definition (SD) videos. HD 3 | /// videos are available for playback in at least 720p, though higher 4 | /// resolutions, like 1080p, might also be available. If you specify a value for 5 | /// this parameter, you must also set the `type` parameter's value to 6 | /// `video`. 7 | /// 8 | /// `string` 9 | /// 10 | /// Acceptable values are: 11 | /// 12 | /// * `any` – Return all videos, regardless of their resolution. 13 | /// * `high` – Only retrieve HD videos. 14 | /// * `standard` – Only retrieve videos in standard definition. 15 | enum VideoDefinition { 16 | any, 17 | high, 18 | standard; 19 | 20 | static VideoDefinition? fromString(String string) => switch (string) { 21 | "hd" => high, 22 | "sd" => standard, 23 | _ => null, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /lib/enum/video_dimension.dart: -------------------------------------------------------------------------------- 1 | import 'package:enum_to_string/enum_to_string.dart'; 2 | 3 | /// The `videoDimension` parameter lets you restrict a search to only retrieve 2D 4 | /// or 3D videos. If you specify a value for this parameter, you must also set 5 | /// the `type` parameter's value to `video`. 6 | /// 7 | /// `string` 8 | /// 9 | /// Acceptable values are: 10 | /// 11 | /// * `2d` – Restrict search results to exclude 3D videos. 12 | /// * `3d` – Restrict search results to only include 3D videos. 13 | /// * `any` – Include both 3D and non-3D videos in returned results. This is 14 | /// the default value. 15 | enum VideoDimension { 16 | twoD('2d'), 17 | threeD('3d'), 18 | any('any'); 19 | 20 | const VideoDimension(this.value); 21 | 22 | final String value; 23 | 24 | static VideoDimension? fromString(String string) => 25 | EnumToString.fromString(values, string); 26 | } 27 | -------------------------------------------------------------------------------- /lib/enum/video_duration.dart: -------------------------------------------------------------------------------- 1 | /// The `videoDuration` parameter filters video search results based on 2 | /// their duration. If you specify a value for this parameter, you must also set 3 | /// the `type` parameter's value to `video`. 4 | /// 5 | /// `string` 6 | /// 7 | /// Acceptable values are: 8 | /// 9 | /// * `any` – Do not filter video search results based on their duration. 10 | /// This is the default value. 11 | /// * `long` – Only include videos longer than 20 minutes. 12 | /// * `medium` – Only include videos that are between four and 20 minutes 13 | /// long (inclusive). 14 | /// * `short` – Only include videos that are less than four minutes long. 15 | enum VideoDuration { any, long, medium, short } 16 | -------------------------------------------------------------------------------- /lib/enum/video_embeddable.dart: -------------------------------------------------------------------------------- 1 | /// The `videoEmbeddable` parameter lets you to restrict a search to only videos 2 | /// that can be embedded into a webpage. If you specify a value for this 3 | /// parameter, you must also set the `type` parameter's value to 4 | /// `video`. 5 | /// 6 | /// `string` 7 | /// 8 | /// Acceptable values are: 9 | /// 10 | /// * `any` – Return all videos, embeddable or not. 11 | /// * `true` – Only retrieve embeddable videos. 12 | enum VideoEmbeddable { any, true_ } 13 | -------------------------------------------------------------------------------- /lib/enum/video_license.dart: -------------------------------------------------------------------------------- 1 | /// The `videoLicense` parameter filters search results to only include videos 2 | /// with a particular license. YouTube lets video uploaders choose to attach 3 | /// either the Creative Commons license or the standard YouTube license to each 4 | /// of their videos. If you specify a value for this parameter, you must also 5 | /// set the `type` parameter's value to `video`. 6 | /// 7 | /// `string` 8 | /// 9 | /// Acceptable values are: 10 | /// 11 | /// * `any` – Return all videos, regardless of which license they have, that 12 | /// match the query parameters. 13 | /// * `creativeCommon` – Only return videos that have a Creative Commons 14 | /// license. Users can reuse videos with this license in other videos that 15 | /// they create. [Learn 16 | /// more](http://www.google.com/support/youtube/bin/answer.py?answer=1284989). 17 | /// * `youtube` – Only return videos that have the standard YouTube license. 18 | enum VideoLicense { any, creativeCommon, youtube } 19 | -------------------------------------------------------------------------------- /lib/enum/video_syndicated.dart: -------------------------------------------------------------------------------- 1 | /// The `videoSyndicated` parameter lets you to restrict a search to only videos 2 | /// that can be played outside youtube.com. If you specify a value for this 3 | /// parameter, you must also set the `type` parameter's value to 4 | /// `video`. 5 | /// 6 | /// `string` 7 | /// 8 | /// Acceptable values are: 9 | /// 10 | /// * `any` – Return all videos, syndicated or not. 11 | /// * `true` – Only retrieve syndicated videos. 12 | enum VideoSyndicated { any, true_ } 13 | -------------------------------------------------------------------------------- /lib/enum/video_type.dart: -------------------------------------------------------------------------------- 1 | /// The `videoType` parameter lets you restrict a search to a particular 2 | /// type of videos. If you specify a value for this parameter, you must also set 3 | /// the `type` parameter's value to `video`. 4 | /// 5 | /// `string` 6 | /// 7 | /// Acceptable values are: 8 | /// 9 | /// * `any` – Return all videos. 10 | /// * `episode` – Only retrieve episodes of shows. 11 | /// * `movie` – Only retrieve movies. 12 | enum VideoType { any, episode, movie } 13 | -------------------------------------------------------------------------------- /lib/model/channel/branding_settings.dart: -------------------------------------------------------------------------------- 1 | class ChannelBrandingSettings { 2 | const ChannelBrandingSettings({ 3 | this.title, 4 | this.description, 5 | this.keywords, 6 | this.trackingAnalyticsAccountId, 7 | this.moderateComments, 8 | this.unsubscribedTrailer, 9 | this.defaultLanguage, 10 | this.country, 11 | }); 12 | factory ChannelBrandingSettings.fromJsonData(Map data) { 13 | final channel = data["channel"] as Map; 14 | final title = channel["title"]; 15 | final description = channel["description"]; 16 | final keywords = _parseKeywords(channel["keywords"] ?? ''); 17 | final trackingAnalyticsAccountId = channel["trackingAnalyticsAccountId"]; 18 | final moderateComments = channel["moderateComments"]; 19 | final unsubscribedTrailer = channel["unsubscribedTrailer"]; 20 | final defaultLanguage = channel["defaultLanguage"]; 21 | final country = channel["country"]; 22 | return ChannelBrandingSettings( 23 | title: title, 24 | description: description, 25 | keywords: keywords, 26 | trackingAnalyticsAccountId: trackingAnalyticsAccountId, 27 | moderateComments: moderateComments, 28 | unsubscribedTrailer: unsubscribedTrailer, 29 | defaultLanguage: defaultLanguage, 30 | country: country, 31 | ); 32 | } 33 | 34 | /// The channel's title. The title has a maximum length of 30 characters. 35 | final String? title; 36 | 37 | /// The channel description, which appears in the channel information box on 38 | /// your channel page. The property's value has a maximum length of 1000 39 | /// characters. 40 | final String? description; 41 | 42 | /// Keywords associated with your channel. The value is a space-separated list 43 | /// of strings. Channel keywords might be truncated if they exceed the maximum 44 | /// allowed length of 500 characters or if they contained unescaped quotation 45 | /// marks ("). Note that the 500 character limit is not a per-keyword limit 46 | /// but rather a limit on the total length of all keywords. 47 | final List? keywords; 48 | 49 | /// The ID for a [Google Analytics 50 | /// account](http://www.google.com/analytics/index.html) that you want to use 51 | /// to track and measure traffic to your channel. 52 | /// 53 | /// `string` 54 | final String? trackingAnalyticsAccountId; 55 | 56 | /// This setting determines whether user-submitted comments left on the 57 | /// channel page need to be approved by the channel owner to be publicly 58 | /// visible. The default value is `false`. 59 | final bool? moderateComments; 60 | 61 | /// The video that should play in the featured video module in the channel 62 | /// page's browse view for unsubscribed viewers. Subscribed viewers may see a 63 | /// different video that highlights more recent channel activity. 64 | /// 65 | /// If specified, the property's value must be the YouTube video ID of a 66 | /// public or unlisted video that is owned by the channel owner. 67 | final String? unsubscribedTrailer; 68 | 69 | /// The language of the text in the `channel` resource's `snippet.title` and 70 | /// `snippet.description` properties. 71 | final String? defaultLanguage; 72 | 73 | /// The country with which the channel is associated. Update this property to 74 | /// set the value of the `snippet.country` property. 75 | final String? country; 76 | 77 | static List _parseKeywords(String keywordsString) { 78 | // Split the input string by space character and remove any empty strings 79 | List keywordsList = keywordsString 80 | .split(' ') 81 | .where((keyword) => keyword.isNotEmpty) 82 | .toList(); 83 | 84 | // Join adjacent strings enclosed in double quotes (") and add them as a 85 | // single keyword 86 | List finalKeywords = []; 87 | String currentKeyword = ''; 88 | 89 | for (var keyword in keywordsList) { 90 | if (keyword.startsWith('"') && keyword.endsWith('"')) { 91 | // If the keyword is enclosed in double quotes, add it as a single 92 | // keyword 93 | finalKeywords.add(keyword.substring(1, keyword.length - 1)); 94 | } else if (keyword.startsWith('"')) { 95 | // If the keyword starts with double quotes, start building the complete 96 | // keyword 97 | currentKeyword = keyword.substring(1); 98 | } else if (keyword.endsWith('"')) { 99 | // If the keyword ends with double quotes, complete the keyword and add 100 | // it to the list 101 | currentKeyword += ' ' + keyword.substring(0, keyword.length - 1); 102 | finalKeywords.add(currentKeyword); 103 | currentKeyword = ''; 104 | } else if (currentKeyword.isNotEmpty) { 105 | // If inside a quoted keyword, continue building it 106 | currentKeyword += ' ' + keyword; 107 | } else { 108 | // Otherwise, it's a normal keyword, add it to the list 109 | finalKeywords.add(keyword); 110 | } 111 | } 112 | 113 | return finalKeywords; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/model/channel/channel_options.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/channel/channel_part.dart'; 2 | import 'package:youtube_api_client/model/options.dart'; 3 | 4 | class ChannelOptions extends SpecificKindOptions { 5 | ChannelOptions({ 6 | this.parts = const {ChannelPart.snippet}, 7 | this.forUsername, 8 | this.managedByMe, 9 | this.mine, 10 | super.id, 11 | super.maxResults, 12 | super.onBehalfOfContentOwner, 13 | super.pageToken, 14 | super.regionCode, 15 | super.hl, 16 | }); 17 | final Set parts; 18 | 19 | /// The `forUsername` parameter specifies a YouTube username, thereby 20 | /// requesting the channel associated with that username. 21 | /// 22 | /// `string` 23 | final String? forUsername; 24 | 25 | /// This parameter can only be used in a properly authorized request. 26 | /// **Note:** This parameter is intended exclusively for YouTube content 27 | /// partners. 28 | /// 29 | /// `boolean` 30 | /// 31 | /// Set this parameter's value to `true` to instruct the API to only return 32 | /// channels managed by the content owner that the `onBehalfOfContentOwner` 33 | /// parameter specifies. The user must be authenticated as a CMS account 34 | /// linked to the specified content owner and `onBehalfOfContentOwner` must be 35 | /// provided. 36 | final bool? managedByMe; 37 | 38 | /// This parameter can only be used in a properly authorized request. Set this 39 | /// parameter's value to `true` to instruct the API to only return channels 40 | /// owned by the authenticated user. 41 | /// 42 | /// `boolean` 43 | final bool? mine; 44 | 45 | @override 46 | Map getMap(String key) => { 47 | ...super.getMap(key), 48 | QueryParameter.part.name: parts.map((part) => part.name).join(','), 49 | if (forUsername != null) QueryParameter.forUsername.name: forUsername!, 50 | if (managedByMe != null) 51 | QueryParameter.managedByMe.name: "$managedByMe", 52 | if (mine != null) QueryParameter.mine.name: "$mine", 53 | }; 54 | 55 | ChannelOptions copyWith({ 56 | Set? parts, 57 | int? maxResults, 58 | String? onBehalfOfContentOwner, 59 | String? pageToken, 60 | String? regionCode, 61 | List? id, 62 | String? hl, 63 | String? forUsername, 64 | bool? managedByMe, 65 | bool? mine, 66 | }) => 67 | ChannelOptions( 68 | parts: parts ?? this.parts, 69 | maxResults: maxResults ?? this.maxResults, 70 | onBehalfOfContentOwner: 71 | onBehalfOfContentOwner ?? this.onBehalfOfContentOwner, 72 | pageToken: pageToken ?? this.pageToken, 73 | regionCode: regionCode ?? this.regionCode, 74 | id: id ?? this.id, 75 | hl: hl ?? this.hl, 76 | forUsername: forUsername ?? this.forUsername, 77 | managedByMe: managedByMe ?? this.managedByMe, 78 | mine: mine ?? this.mine, 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /lib/model/channel/channel_snippet.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/model/snippet.dart'; 2 | import 'package:youtube_api_client/model/thumbnails/thumbnail.dart'; 3 | 4 | class ChannelSnippet extends Snippet { 5 | const ChannelSnippet({ 6 | super.publishedAt, 7 | super.title, 8 | super.description, 9 | super.thumbnails, 10 | super.defaultLanguage, 11 | this.customUrl, 12 | this.country, 13 | }); 14 | 15 | factory ChannelSnippet.fromJsonData(Map data) { 16 | final thumbnails = Thumbnail.thumbnailsFromMap(data['thumbnails']); 17 | final publishedAt = DateTime.tryParse(data['publishedAt']); 18 | final description = data['description']; 19 | final title = data['title']; 20 | final customUrl = data['customUrl']; 21 | final country = data['country']; 22 | 23 | return ChannelSnippet( 24 | thumbnails: thumbnails, 25 | publishedAt: publishedAt, 26 | description: description, 27 | title: title, 28 | customUrl: customUrl, 29 | country: country, 30 | ); 31 | } 32 | 33 | /// The channel's custom URL. The [YouTube Help 34 | /// Center](https://support.google.com/youtube/answer/2657968) explains 35 | /// eligibility requirements for getting a custom URL as well as how to set up 36 | /// the URL. 37 | final String? customUrl; 38 | 39 | /// The country with which the channel is associated. To set this property's 40 | /// value, update the value of the `brandingSettings.channel.country` 41 | /// property. 42 | final String? country; 43 | } 44 | -------------------------------------------------------------------------------- /lib/model/channel/youtube_channel.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/result_type.dart'; 2 | import 'package:youtube_api_client/model/channel/branding_settings.dart'; 3 | import 'package:youtube_api_client/model/channel/channel_snippet.dart'; 4 | import 'package:youtube_api_client/model/youtube_api_result.dart'; 5 | 6 | class YoutubeChannel extends ApiResult { 7 | YoutubeChannel(data, {bool isSingleResult = false}) 8 | : super(data, isSingleResult: isSingleResult) { 9 | final snippet_ = data["snippet"]; 10 | if (snippet_ != null) snippet = ChannelSnippet.fromJsonData(snippet_); 11 | final brandingSettings_ = data["brandingSettings"]; 12 | if (brandingSettings_ != null) 13 | brandingSettings = 14 | ChannelBrandingSettings.fromJsonData(brandingSettings_); 15 | } 16 | covariant late final ChannelSnippet? snippet; 17 | late final ChannelBrandingSettings? brandingSettings; 18 | @override 19 | ResultType get type => ResultType.channel; 20 | } 21 | -------------------------------------------------------------------------------- /lib/model/options.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/query_parameter.dart'; 2 | 3 | export 'package:youtube_api_client/enum/query_parameter.dart'; 4 | 5 | abstract class Options { 6 | const Options({ 7 | this.maxResults, 8 | this.onBehalfOfContentOwner, 9 | this.pageToken, 10 | this.regionCode, 11 | }); 12 | 13 | /// The `maxResults` parameter specifies the maximum number of items that 14 | /// should be returned in the result set. Acceptable values are `0` to `50`, 15 | /// inclusive. The default value is `5`. 16 | /// 17 | /// `unsigned integer` 18 | final int? maxResults; 19 | 20 | /// This parameter can only be used in a properly authorized 21 | /// request. **Note:** This parameter is 22 | /// intended exclusively for YouTube content partners. 23 | /// 24 | /// `string` 25 | /// 26 | /// The `onBehalfOfContentOwner` parameter indicates that the request's 27 | /// authorization credentials identify a YouTube CMS user who is acting on 28 | /// behalf of the content owner specified in the parameter value. This 29 | /// parameter is intended for YouTube content partners that own and manage 30 | /// many different YouTube channels. It allows content owners to authenticate 31 | /// once and get access to all their video and channel data, without having to 32 | /// provide authentication credentials for each individual channel. The CMS 33 | /// account that the user authenticates with must be linked to the specified 34 | /// YouTube content owner. 35 | final String? onBehalfOfContentOwner; 36 | 37 | /// The `pageToken` parameter identifies a specific page in the result set 38 | /// that should be returned. In an API response, the `nextPageToken` and 39 | /// `prevPageToken` properties identify other pages that could be retrieved. 40 | /// 41 | /// `string` 42 | final String? pageToken; 43 | 44 | /// The `regionCode` parameter instructs the API to return search results for 45 | /// videos that can be viewed in the specified country. The parameter value is 46 | /// an ISO 3166-1 alpha-2 country code. 47 | /// 48 | /// `string` 49 | final String? regionCode; 50 | 51 | Map getMap(String key) => { 52 | "key": key, 53 | if (maxResults != null) QueryParameter.maxResults.name: "$maxResults", 54 | if (onBehalfOfContentOwner != null) 55 | QueryParameter.onBehalfOfContentOwner.name: onBehalfOfContentOwner!, 56 | if (pageToken != null) QueryParameter.pageToken.name: pageToken!, 57 | if (regionCode != null) QueryParameter.regionCode.name: regionCode!, 58 | }; 59 | } 60 | 61 | abstract class SpecificKindOptions extends Options { 62 | const SpecificKindOptions({ 63 | this.id, 64 | this.hl, 65 | super.maxResults, 66 | super.onBehalfOfContentOwner, 67 | super.pageToken, 68 | super.regionCode, 69 | }); 70 | 71 | /// The `id` parameter specifies a comma-separated list of the YouTube 72 | /// video ID(s) for the resource(s) that are being retrieved. In a `video` 73 | /// resource, the `id` property specifies the video's ID. 74 | /// 75 | /// `string` 76 | final List? id; 77 | 78 | /// The `hl` parameter instructs the API to retrieve localized resource 79 | /// metadata for a specific[application language that the YouTube website 80 | /// supports. The parameter value must be a language code included in the list 81 | /// returned by the `i18nLanguages.list` method. 82 | /// 83 | /// `string` 84 | /// 85 | /// If localized resource details are available in that language, the 86 | /// resource's `snippet.localized` object will contain the localized values. 87 | /// However, if localized details are not available, the `snippet.localized` 88 | /// object will contain resource details in the resource's default language. 89 | final String? hl; 90 | 91 | @override 92 | Map getMap(String key) => { 93 | ...super.getMap(key), 94 | if (id != null) QueryParameter.id.name: id!.join(','), 95 | if (hl != null) QueryParameter.hl.name: hl!, 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /lib/model/playlist/playlist_options.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/playlist/playlist_part.dart'; 2 | import 'package:youtube_api_client/model/options.dart'; 3 | 4 | class PlaylistOptions extends SpecificKindOptions { 5 | const PlaylistOptions({ 6 | this.parts = const {PlaylistPart.snippet}, 7 | super.id, 8 | super.hl, 9 | super.maxResults, 10 | super.onBehalfOfContentOwner, 11 | super.pageToken, 12 | super.regionCode, 13 | }); 14 | final Set parts; 15 | 16 | @override 17 | Map getMap(String key) => { 18 | ...super.getMap(key), 19 | QueryParameter.part.name: parts.map((part) => part.name).join(','), 20 | }; 21 | 22 | PlaylistOptions copyWith({ 23 | Set? parts, 24 | int? maxResults, 25 | String? onBehalfOfContentOwner, 26 | String? pageToken, 27 | String? regionCode, 28 | List? id, 29 | String? hl, 30 | String? forUsername, 31 | bool? managedByMe, 32 | bool? mine, 33 | }) => 34 | PlaylistOptions( 35 | parts: parts ?? this.parts, 36 | maxResults: maxResults ?? this.maxResults, 37 | onBehalfOfContentOwner: 38 | onBehalfOfContentOwner ?? this.onBehalfOfContentOwner, 39 | pageToken: pageToken ?? this.pageToken, 40 | regionCode: regionCode ?? this.regionCode, 41 | id: id ?? this.id, 42 | hl: hl ?? this.hl, 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /lib/model/playlist/playlist_snippet.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/model/snippet.dart'; 2 | import 'package:youtube_api_client/model/thumbnails/thumbnail.dart'; 3 | 4 | class PlaylistSnippet extends ChannelBelongSnippet { 5 | const PlaylistSnippet({ 6 | super.publishedAt, 7 | super.title, 8 | super.description, 9 | super.thumbnails, 10 | super.defaultLanguage, 11 | super.channelId, 12 | super.channelTitle, 13 | }); 14 | factory PlaylistSnippet.fromJsonData(Map data) { 15 | final thumbnails = Thumbnail.thumbnailsFromMap(data['thumbnails']); 16 | final publishedAt = DateTime.tryParse(data['publishedAt']); 17 | final description = data['description']; 18 | final title = data['title']; 19 | 20 | return PlaylistSnippet( 21 | thumbnails: thumbnails, 22 | publishedAt: publishedAt, 23 | description: description, 24 | title: title, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/model/playlist/youtube_playlist.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/result_type.dart'; 2 | import 'package:youtube_api_client/model/youtube_api_result.dart'; 3 | import 'package:youtube_api_client/playlist.dart'; 4 | 5 | class YoutubePlaylist extends ApiResult { 6 | YoutubePlaylist(data, {bool isSingleResult = false}) 7 | : super(data, isSingleResult: isSingleResult) { 8 | final snippet_ = data["snippet"]; 9 | if (snippet_ != null) snippet = PlaylistSnippet.fromJsonData(snippet_); 10 | } 11 | covariant late final PlaylistSnippet? snippet; 12 | @override 13 | ResultType get type => ResultType.playlist; 14 | } 15 | -------------------------------------------------------------------------------- /lib/model/snippet.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/model/thumbnails/thumbnail.dart'; 2 | import 'package:youtube_api_client/model/thumbnails/thumbnail_resolution.dart'; 3 | 4 | abstract class Snippet { 5 | const Snippet({ 6 | this.publishedAt, 7 | this.title, 8 | this.description, 9 | this.thumbnails, 10 | this.defaultLanguage, 11 | }); 12 | 13 | /// The date and time that the video was published. Note that this time might 14 | /// be different than the time that the video was uploaded. For example, if a 15 | /// video is uploaded as a private video and then made public at a later time, 16 | /// this property will specify the time that the video was made public. 17 | /// 18 | /// `datetime` 19 | /// 20 | /// There are a couple of special cases: 21 | /// 22 | /// * If a video is uploaded as a private video and the video metadata is 23 | /// retrieved by the channel owner, then the property value specifies the 24 | /// date and time that the video was uploaded. 25 | /// * If a video is uploaded as an unlisted video, the property value also 26 | /// specifies the date and time that the video was uploaded. In this case, 27 | /// anyone who knows the video's unique video ID can retrieve the video 28 | /// metadata. 29 | /// 30 | /// The value is specified in [ISO 8601](https://www.w3.org/TR/NOTE-datetime) 31 | /// format. 32 | final DateTime? publishedAt; 33 | 34 | /// The video's title. The property value has a maximum length of 100 35 | /// characters and may contain all valid UTF-8 characters except `<` and `>`. 36 | /// You must set a value for this property if you call the `videos.update` 37 | /// method and are updating the `snippet` part of a `video` resource. 38 | final String? title; 39 | 40 | /// The video's description. The property value has a maximum length of 5000 41 | /// bytes and may contain all valid UTF-8 characters except `<` and `>`. 42 | final String? description; 43 | 44 | /// A map of thumbnail images associated with the video. For each object in 45 | /// the map, the key is the name of the thumbnail image, and the value is an 46 | /// object that contains other information about the thumbnail. 47 | final Map? thumbnails; 48 | 49 | /// The language of the text in the `video` resource's `snippet.title` and 50 | /// `snippet.description` properties. 51 | final String? defaultLanguage; 52 | 53 | // TODO(hyungtaecf): Implement localized 54 | // "localized": { 55 | // "title": string, 56 | // "description": string 57 | // }, 58 | } 59 | 60 | abstract class ChannelBelongSnippet extends Snippet { 61 | const ChannelBelongSnippet({ 62 | super.publishedAt, 63 | super.title, 64 | super.description, 65 | super.thumbnails, 66 | super.defaultLanguage, 67 | this.channelId, 68 | this.channelTitle, 69 | }); 70 | 71 | /// The ID that YouTube uses to uniquely identify the channel that the video 72 | /// was uploaded to. 73 | final String? channelId; 74 | 75 | /// Channel title for the channel that the video belongs to. 76 | final String? channelTitle; 77 | } 78 | -------------------------------------------------------------------------------- /lib/model/thumbnails/thumbnail.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:youtube_api_client/model/thumbnails/thumbnail_resolution.dart'; 4 | 5 | class Thumbnail { 6 | Thumbnail({ 7 | required this.url, 8 | required this.width, 9 | required this.height, 10 | }); 11 | 12 | factory Thumbnail.fromMap(Map map) { 13 | return Thumbnail( 14 | url: map['url'], 15 | width: map['width'], 16 | height: map['height'], 17 | ); 18 | } 19 | 20 | /// The image's URL. 21 | final String? url; 22 | 23 | /// The image's width. 24 | final int? width; 25 | 26 | /// The image's height. 27 | final int? height; 28 | 29 | Map toMap() => { 30 | 'url': url, 31 | 'width': width, 32 | 'height': height, 33 | }; 34 | 35 | static Map thumbnailsFromMap( 36 | Map map) { 37 | final default_ = map['default']; 38 | final medium = map['medium']; 39 | final high = map['high']; 40 | final standard = map['standard']; 41 | final maxres = map['maxres']; 42 | return { 43 | if (default_ != null) 44 | ThumbnailResolution.default_: Thumbnail.fromMap(default_), 45 | if (medium != null) ThumbnailResolution.medium: Thumbnail.fromMap(medium), 46 | if (high != null) ThumbnailResolution.high: Thumbnail.fromMap(high), 47 | if (standard != null) 48 | ThumbnailResolution.standard: Thumbnail.fromMap(standard), 49 | if (maxres != null) ThumbnailResolution.maxres: Thumbnail.fromMap(maxres), 50 | }; 51 | } 52 | 53 | String toJson() => json.encode(toMap()); 54 | 55 | factory Thumbnail.fromJson(String source) => 56 | Thumbnail.fromMap(json.decode(source)); 57 | } 58 | -------------------------------------------------------------------------------- /lib/model/thumbnails/thumbnail_resolution.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:youtube_api_client/enum/result_type.dart'; 4 | 5 | enum ThumbnailResolution { 6 | /// The default thumbnail image. The default thumbnail for a video – or a 7 | /// resource that refers to a video, such as a playlist item or search result 8 | /// – is 120px wide and 90px tall. The default thumbnail for a channel is 88px 9 | /// wide and 88px tall. 10 | default_, 11 | 12 | /// A higher resolution version of the thumbnail image. For a video (or a 13 | /// resource that refers to a video), this image is 320px wide and 180px tall. 14 | /// For a channel, this image is 240px wide and 240px tall. 15 | medium, 16 | 17 | /// A high resolution version of the thumbnail image. For a video (or a 18 | /// resource that refers to a video), this image is 480px wide and 360px tall. 19 | /// For a channel, this image is 800px wide and 800px tall. 20 | high, 21 | 22 | /// An even higher resolution version of the thumbnail image than the high 23 | /// resolution image. This image is available for some videos and other 24 | /// resources that refer to videos, like playlist items or search results. 25 | /// This image is 640px wide and 480px tall. 26 | standard, 27 | 28 | /// The highest resolution version of the thumbnail image. This image size is 29 | /// available for some videos and other resources that refer to videos, like 30 | /// playlist items or search results. This image is 1280px wide and 720px 31 | /// tall. 32 | maxres; 33 | 34 | Set validValuesFor(ResultType type) => switch (type) { 35 | ResultType.video || ResultType.playlist => values.toSet(), 36 | ResultType.channel => {default_, medium, high}, 37 | }; 38 | 39 | /// Resolution size in pixels. 40 | Size getSizeFor(ResultType type) => switch (this) { 41 | default_ => switch (type) { 42 | ResultType.channel => Size.square(88), 43 | _ => Size(120, 90), 44 | }, 45 | medium => switch (type) { 46 | ResultType.channel => Size.square(240), 47 | _ => Size(320, 180), 48 | }, 49 | high => switch (type) { 50 | ResultType.channel => Size.square(800), 51 | _ => Size(480, 360), 52 | }, 53 | standard => Size(640, 480), 54 | maxres => Size(1280, 720), 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /lib/model/video/video_content_details.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/util/duration_extension.dart'; 2 | import 'package:youtube_api_client/video.dart'; 3 | 4 | /// The contentDetails object contains information about the video content, 5 | /// including the length of the video and an indication of whether captions are 6 | /// available for the video. 7 | class VideoContentDetails { 8 | VideoContentDetails({ 9 | this.duration, 10 | this.dimension, 11 | this.definition, 12 | this.caption, 13 | this.licensedContent, 14 | this.projection, 15 | }); 16 | factory VideoContentDetails.fromJsonData(Map data) { 17 | final duration = 18 | DurationExtension.fromIsoDurationString(data["duration"] ?? ''); 19 | final dimension = VideoDimension.fromString(data["dimension"] ?? ''); 20 | final definition = VideoDefinition.fromString(data["definition"] ?? ''); 21 | final caption = VideoCaption.fromString(data["caption"] ?? ''); 22 | final licensedContent = data["licensedContent"]; 23 | final projection = VideoProjection.fromString(data["projection"] ?? ''); 24 | 25 | return VideoContentDetails( 26 | duration: duration, 27 | dimension: dimension, 28 | definition: definition, 29 | caption: caption, 30 | licensedContent: licensedContent, 31 | projection: projection, 32 | ); 33 | } 34 | 35 | /// The length of the video. The property value is an ISO 8601 duration. For 36 | /// example, for a video that is at least one minute long and less than one 37 | /// hour long, the duration is in the format `PT#M#S`, in which the letters 38 | /// `PT` indicate that the value specifies a period of time, and the letters 39 | /// `M` and `S` refer to length in minutes and seconds, respectively. The `#` 40 | /// characters preceding the `M` and `S` letters are both integers that 41 | /// specify the number of minutes (or seconds) of the video. For example, a 42 | /// value of `PT15M33S` indicates that the video is 15 minutes and 33 seconds 43 | /// long. 44 | /// 45 | /// If the video is at least one hour long, the duration is in the format 46 | /// `PT#H#M#S`, in which the `#` preceding the letter `H` specifies the length 47 | /// of the video in hours and all of the other details are the same as 48 | /// described above. If the video is at least one day long, the letters `P` 49 | /// and `T` are separated, and the value's format is `P#DT#H#M#S`. Please 50 | /// refer to the ISO 8601 specification for complete details. 51 | final Duration? duration; 52 | 53 | /// Indicates whether the video is available in 3D or in 2D. 54 | final VideoDimension? dimension; 55 | 56 | /// Indicates whether the video is available in high definition (HD) or only 57 | /// in standard definition. 58 | final VideoDefinition? definition; 59 | 60 | /// Indicates whether captions are available for the video. 61 | final VideoCaption? caption; 62 | 63 | /// Indicates whether the video represents licensed content, which means that 64 | /// the content was uploaded to a channel linked to a YouTube content partner 65 | /// and then claimed by that partner. 66 | final bool? licensedContent; 67 | 68 | /// Specifies the projection format of the video. 69 | final VideoProjection? projection; 70 | 71 | // TODO(hyungtaecf): Implement regionRestriction 72 | // "regionRestriction": { 73 | // "allowed": [ 74 | // string 75 | // ], 76 | // "blocked": [ 77 | // string 78 | // ] 79 | // } 80 | 81 | // TODO(hyungtaecf): Implement contentRating 82 | // TODO(hyungtaecf): Implement hasCustomThumbnail 83 | } 84 | -------------------------------------------------------------------------------- /lib/model/video/video_options.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/video/chart.dart'; 2 | import 'package:youtube_api_client/enum/video/my_rating.dart'; 3 | import 'package:youtube_api_client/enum/video/video_part.dart'; 4 | import 'package:youtube_api_client/model/options.dart'; 5 | 6 | class VideoOptions extends SpecificKindOptions { 7 | const VideoOptions({ 8 | this.parts = const {VideoPart.snippet}, 9 | this.chart, 10 | super.id, 11 | this.myRating, 12 | super.maxResults, 13 | super.onBehalfOfContentOwner, 14 | super.pageToken, 15 | super.regionCode, 16 | super.hl, 17 | this.maxHeight, 18 | this.maxWidth, 19 | }); 20 | 21 | final Set parts; 22 | 23 | final Chart? chart; 24 | 25 | final MyRating? myRating; 26 | 27 | /// The `maxHeight` parameter specifies the maximum height of the embedded 28 | /// player returned in the `player.embedHtml` property. You can use this 29 | /// parameter to specify that instead of the default dimensions, the embed 30 | /// code should use a height appropriate for your application layout. If the 31 | /// `maxWidth` parameter is also provided, the player may be shorter than the 32 | /// `maxHeight` in order to not violate the maximum width. Acceptable values 33 | /// are `72` to `8192`, inclusive. 34 | /// 35 | /// `unsigned integer` 36 | final int? maxHeight; 37 | 38 | /// The `maxWidth` parameter specifies the maximum width of the embedded 39 | /// player returned in the `player.embedHtml` property. You can use this 40 | /// parameter to specify that instead of the default dimensions, the embed 41 | /// code should use a width appropriate for your application layout. 42 | /// 43 | /// `unsigned integer` 44 | /// 45 | /// If the `maxHeight` parameter is also provided, the player may be narrower 46 | /// than `maxWidth` in order to not violate the maximum height. Acceptable 47 | /// values are `72` to `8192`, inclusive. 48 | final String? maxWidth; 49 | 50 | @override 51 | Map getMap(String key) => { 52 | ...super.getMap(key), 53 | QueryParameter.part.name: parts.map((part) => part.name).join(','), 54 | if (chart != null) QueryParameter.chart.name: chart!.name, 55 | if (id != null) QueryParameter.id.name: id!.join(','), 56 | if (myRating != null) QueryParameter.myRating.name: myRating!.name, 57 | if (hl != null) QueryParameter.hl.name: hl!, 58 | if (maxHeight != null) QueryParameter.maxHeight.name: "$maxHeight", 59 | if (maxWidth != null) QueryParameter.maxWidth.name: "$maxWidth", 60 | }; 61 | 62 | VideoOptions copyWith({ 63 | Set? parts, 64 | int? maxResults, 65 | String? onBehalfOfContentOwner, 66 | String? pageToken, 67 | String? regionCode, 68 | Chart? chart, 69 | List? id, 70 | MyRating? myRating, 71 | String? hl, 72 | int? maxHeight, 73 | String? maxWidth, 74 | }) => 75 | VideoOptions( 76 | parts: parts ?? this.parts, 77 | maxResults: maxResults ?? this.maxResults, 78 | onBehalfOfContentOwner: 79 | onBehalfOfContentOwner ?? this.onBehalfOfContentOwner, 80 | pageToken: pageToken ?? this.pageToken, 81 | regionCode: regionCode ?? this.regionCode, 82 | chart: chart ?? this.chart, 83 | id: id ?? this.id, 84 | myRating: myRating ?? this.myRating, 85 | hl: hl ?? this.hl, 86 | maxHeight: maxHeight ?? this.maxHeight, 87 | maxWidth: maxWidth ?? this.maxWidth, 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /lib/model/video/video_snippet.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/category.dart'; 2 | import 'package:youtube_api_client/enum/result_type.dart'; 3 | import 'package:youtube_api_client/enum/video/live_broadcast_content.dart'; 4 | import 'package:youtube_api_client/model/snippet.dart'; 5 | import 'package:youtube_api_client/model/thumbnails/thumbnail.dart'; 6 | import 'package:youtube_api_client/model/youtube_api_result.dart'; 7 | 8 | class VideoSnippet extends ChannelBelongSnippet { 9 | const VideoSnippet({ 10 | super.publishedAt, 11 | super.channelId, 12 | super.title, 13 | super.description, 14 | super.thumbnails, 15 | super.channelTitle, 16 | this.tags, 17 | this.category, 18 | this.liveBroadcastContent, 19 | super.defaultLanguage, 20 | this.defaultAudioLanguage, 21 | }); 22 | 23 | factory VideoSnippet.fromJsonData(Map data) { 24 | final thumbnails = Thumbnail.thumbnailsFromMap(data['thumbnails']); 25 | final tags = List.from(data['tags'] ?? []); 26 | final publishedAt = DateTime.tryParse(data['publishedAt']); 27 | final description = data['description']; 28 | final title = data['title']; 29 | final channelId = data['channelId']; 30 | final channelTitle = data['channelTitle']; 31 | final category = 32 | Category.fromCategoryId(int.tryParse(data['categoryId'] ?? "") ?? -1); 33 | 34 | return VideoSnippet( 35 | thumbnails: thumbnails, 36 | tags: tags, 37 | publishedAt: publishedAt, 38 | description: description, 39 | title: title, 40 | channelId: channelId, 41 | channelTitle: channelTitle, 42 | category: category, 43 | ); 44 | } 45 | 46 | /// A list of keyword tags associated with the video. Tags may contain spaces. 47 | /// The property value has a maximum length of 500 characters. Note the 48 | /// following rules regarding the way the character limit is calculated: 49 | /// 50 | /// `list` 51 | /// 52 | /// * The property value is a list, and commas between items in the list 53 | /// count toward the limit. 54 | /// * If a tag contains a space, the API server handles the tag value as 55 | /// though it were wrapped in quotation marks, and the quotation marks 56 | /// count toward the character limit. So, for the purposes of character 57 | /// limits, the tag **Foo-Baz** contains seven characters, but the tag 58 | /// **Foo Baz** contains nine characters. 59 | final List? tags; 60 | 61 | /// The YouTube video category associated with the video. You must set a value 62 | /// for this property if you call the `videos.update` method and are updating 63 | /// the `snippet` part of a `video` resource. 64 | /// 65 | /// `string` 66 | final Category? category; 67 | 68 | /// Indicates if the video is an upcoming/active live broadcast. Or it's 69 | /// "none" if the video is not an upcoming/active live broadcast. 70 | final LiveBroadcastContent? liveBroadcastContent; 71 | 72 | /// The `default_audio_language` property specifies the language spoken in the 73 | /// video's default audio track. 74 | final String? defaultAudioLanguage; 75 | 76 | String? get channelUrl => channelId == null 77 | ? null 78 | : ApiResult.getURL(ResultType.channel, channelId!); 79 | } 80 | -------------------------------------------------------------------------------- /lib/model/video/youtube_video.dart: -------------------------------------------------------------------------------- 1 | import 'package:youtube_api_client/enum/result_type.dart'; 2 | import 'package:youtube_api_client/model/video/video_content_details.dart'; 3 | import 'package:youtube_api_client/model/video/video_snippet.dart'; 4 | import 'package:youtube_api_client/model/youtube_api_result.dart'; 5 | 6 | class YoutubeVideo extends ApiResult { 7 | YoutubeVideo(data, {bool isSingleResult = false}) 8 | : super(data, isSingleResult: isSingleResult) { 9 | final snippet_ = data["snippet"]; 10 | if (snippet_ != null) snippet = VideoSnippet.fromJsonData(snippet_); 11 | final contentDetails_ = data["contentDetails"]; 12 | if (contentDetails_ != null) 13 | contentDetails = VideoContentDetails.fromJsonData(contentDetails_); 14 | } 15 | late final VideoContentDetails? contentDetails; 16 | covariant late final VideoSnippet? snippet; 17 | 18 | @override 19 | ResultType get type => ResultType.video; 20 | 21 | String getURL() => ApiResult.getURL(ResultType.video, id); 22 | } 23 | -------------------------------------------------------------------------------- /lib/model/youtube_api_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:enum_to_string/enum_to_string.dart'; 2 | import 'package:youtube_api_client/enum/result_type.dart'; 3 | import 'package:youtube_api_client/model/channel/youtube_channel.dart'; 4 | import 'package:youtube_api_client/model/playlist/youtube_playlist.dart'; 5 | import 'package:youtube_api_client/model/snippet.dart'; 6 | import 'package:youtube_api_client/model/video/youtube_video.dart'; 7 | 8 | abstract class ApiResult { 9 | late String id; 10 | 11 | late final Snippet? snippet; 12 | 13 | static const baseURL = "https://www.youtube.com/"; 14 | 15 | ApiResult(data, {bool isSingleResult = false}) { 16 | id = isSingleResult ? data['id'] : data['id'][data['id'].keys.elementAt(1)]; 17 | } 18 | 19 | factory ApiResult.fromMap(dynamic data, {bool isSpecificKind = false}) { 20 | final type = EnumToString.fromString( 21 | ResultType.values, 22 | ((isSpecificKind ? data['kind'] : data['id']['kind']) as String) 23 | .split('#') 24 | .last)!; 25 | return switch (type) { 26 | ResultType.video => YoutubeVideo(data, isSingleResult: isSpecificKind), 27 | ResultType.channel => 28 | YoutubeChannel(data, isSingleResult: isSpecificKind), 29 | ResultType.playlist => 30 | YoutubePlaylist(data, isSingleResult: isSpecificKind), 31 | }; 32 | } 33 | 34 | ResultType get type; 35 | 36 | String get url => getURL(type, id); 37 | 38 | static String getURL(ResultType kind, String id) => switch (kind) { 39 | ResultType.channel => "${baseURL}channel/$id", 40 | ResultType.video => "${baseURL}watch?v=$id", 41 | ResultType.playlist => "${baseURL}playlist?list=$id", 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /lib/playlist.dart: -------------------------------------------------------------------------------- 1 | export 'package:youtube_api_client/model/playlist/playlist_snippet.dart'; 2 | export 'package:youtube_api_client/model/playlist/playlist_options.dart'; 3 | export 'package:youtube_api_client/model/playlist/youtube_playlist.dart'; 4 | export 'package:youtube_api_client/enum/playlist/playlist_part.dart'; 5 | -------------------------------------------------------------------------------- /lib/search.dart: -------------------------------------------------------------------------------- 1 | export 'package:youtube_api_client/enum/search/search_part.dart'; 2 | export 'package:youtube_api_client/enum/category.dart'; 3 | export 'package:youtube_api_client/enum/result_type.dart'; 4 | export 'package:youtube_api_client/enum/search/channel_type.dart'; 5 | export 'package:youtube_api_client/enum/search/event_type.dart'; 6 | export 'package:youtube_api_client/enum/search/order.dart'; 7 | export 'package:youtube_api_client/enum/search/safe_search.dart'; 8 | export 'package:youtube_api_client/enum/search/topic.dart'; 9 | export 'package:youtube_api_client/enum/video_caption.dart'; 10 | export 'package:youtube_api_client/enum/video_definition.dart'; 11 | export 'package:youtube_api_client/enum/video_dimension.dart'; 12 | export 'package:youtube_api_client/enum/video_duration.dart'; 13 | export 'package:youtube_api_client/enum/video_embeddable.dart'; 14 | export 'package:youtube_api_client/enum/video_license.dart'; 15 | export 'package:youtube_api_client/enum/video_syndicated.dart'; 16 | export 'package:youtube_api_client/enum/video_type.dart'; 17 | export 'package:youtube_api_client/model/search/search_options.dart'; 18 | -------------------------------------------------------------------------------- /lib/util/duration_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:iso8601_duration/iso8601_duration.dart'; 2 | 3 | extension DurationExtension on Duration { 4 | static Duration fromIsoDurationString(String duration) { 5 | final ISODuration isoDuration = 6 | ISODurationConverter().parseString(isoDurationString: duration); 7 | return Duration( 8 | days: isoDuration.day?.toInt() ?? 0, 9 | hours: isoDuration.hour?.toInt() ?? 0, 10 | minutes: isoDuration.minute?.toInt() ?? 0, 11 | seconds: isoDuration.seconds?.toInt() ?? 0, 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/video.dart: -------------------------------------------------------------------------------- 1 | export 'package:youtube_api_client/enum/video/video_part.dart'; 2 | export 'package:youtube_api_client/enum/video/chart.dart'; 3 | export 'package:youtube_api_client/enum/video/my_rating.dart'; 4 | export 'package:youtube_api_client/enum/video_caption.dart'; 5 | export 'package:youtube_api_client/enum/video_definition.dart'; 6 | export 'package:youtube_api_client/enum/video_dimension.dart'; 7 | export 'package:youtube_api_client/enum/video_duration.dart'; 8 | export 'package:youtube_api_client/enum/video_embeddable.dart'; 9 | export 'package:youtube_api_client/enum/video_license.dart'; 10 | export 'package:youtube_api_client/enum/video_syndicated.dart'; 11 | export 'package:youtube_api_client/enum/video_type.dart'; 12 | export 'package:youtube_api_client/enum/video/video_projection.dart'; 13 | export 'package:youtube_api_client/model/video/video_options.dart'; 14 | export 'package:youtube_api_client/model/video/youtube_video.dart'; 15 | export 'package:youtube_api_client/enum/category.dart'; 16 | export 'package:youtube_api_client/enum/video/live_broadcast_content.dart'; 17 | export 'package:youtube_api_client/model/thumbnails/thumbnail_resolution.dart'; 18 | export 'package:youtube_api_client/model/thumbnails/thumbnail.dart'; 19 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: youtube_api_client 2 | description: 3 | YouTube API Client is a plugin which directly interacts with YouTube 4 | server. Supports searching videos, channels and playlists. 5 | version: 1.1.0 6 | repository: https://github.com/hyungtaecf/youtube_api 7 | 8 | environment: 9 | sdk: ">=3.0.0 <4.0.0" 10 | flutter: ">=1.20.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | http: ^1.2.1 16 | collection: ^1.15.0 17 | enum_to_string: ^2.0.1 18 | iso8601_duration: ^0.0.4 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | 24 | screenshots: 25 | - description: "Demo showing API call result." 26 | path: "screenshots/demo.jpg" 27 | 28 | topics: 29 | - youtube 30 | - youtube-api 31 | - api 32 | - video 33 | -------------------------------------------------------------------------------- /screenshots/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nstack-in/youtube_api/91b3425e65e3479c6e3962c36fdd7b0f34bcde66/screenshots/demo.jpg -------------------------------------------------------------------------------- /test/youtube_api_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /youtube_api_client.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------