From 91871d0d2632370720db0f00ceb157e3cfd134e2 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 7 Mar 2025 11:50:39 +0600 Subject: [PATCH] feat: custom piped & invidious instance support --- .../adaptive/adaptive_select_tile.dart | 21 +- lib/l10n/app_en.arb | 852 +++++++++--------- lib/pages/settings/sections/playback.dart | 287 +++++- untranslated_messages.json | 104 ++- 4 files changed, 764 insertions(+), 500 deletions(-) diff --git a/lib/components/adaptive/adaptive_select_tile.dart b/lib/components/adaptive/adaptive_select_tile.dart index 80e4ae80..afa982af 100644 --- a/lib/components/adaptive/adaptive_select_tile.dart +++ b/lib/components/adaptive/adaptive_select_tile.dart @@ -8,6 +8,7 @@ class AdaptiveSelectTile extends HookWidget { final Widget title; final Widget? subtitle; final Widget? secondary; + final List? trailing; final ListTileControlAffinity? controlAffinity; final T value; final ValueChanged? onChanged; @@ -34,6 +35,7 @@ class AdaptiveSelectTile extends HookWidget { this.controlAffinity = ListTileControlAffinity.trailing, this.subtitle, this.secondary, + this.trailing, this.breakLayout, this.showValueWhenUnfolded = true, super.key, @@ -54,8 +56,10 @@ class AdaptiveSelectTile extends HookWidget { onChanged: onChanged, popupConstraints: popupConstraints ?? const BoxConstraints(maxWidth: 200), popupWidthConstraint: popupWidthConstraint ?? PopoverConstraint.flexible, + autoClosePopover: true, popup: (context) { return SelectPopup( + autoClose: true, items: SelectItemBuilder( childCount: options.length, builder: (context, index) { @@ -82,9 +86,20 @@ class AdaptiveSelectTile extends HookWidget { leading: controlAffinity != ListTileControlAffinity.leading ? secondary : control, - trailing: controlAffinity == ListTileControlAffinity.leading - ? secondary - : control, + trailing: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + spacing: 5, + children: [ + ...?trailing, + if (controlAffinity == ListTileControlAffinity.leading && + secondary != null) + secondary! + else if (controlAffinity == ListTileControlAffinity.trailing && + control != null) + control, + ], + ), onTap: breakLayout ?? mediaQuery.mdAndUp ? null : () { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 98dd5d5f..e3e6d330 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,426 +1,428 @@ { - "guest": "Guest", - "browse": "Browse", - "search": "Search", - "library": "Library", - "lyrics": "Lyrics", - "settings": "Settings", - "genre_categories_filter": "Filter categories or genres...", - "genre": "Genre", - "personalized": "Personalized", - "featured": "Featured", - "new_releases": "New Releases", - "songs": "Songs", - "playing_track": "Playing {track}", - "queue_clear_alert": "This will clear the current queue. {track_length} tracks will be removed\nDo you want to continue?", - "load_more": "Load more", - "playlists": "Playlists", - "artists": "Artists", - "albums": "Albums", - "tracks": "Tracks", - "downloads": "Downloads", - "filter_playlists": "Filter your playlists...", - "liked_tracks": "Liked Tracks", - "liked_tracks_description": "All your liked tracks", - "playlist": "Playlist", - "create_a_playlist": "Create a playlist", - "update_playlist": "Update playlist", - "create": "Create", - "cancel": "Cancel", - "update": "Update", - "playlist_name": "Playlist Name", - "name_of_playlist": "Name of the playlist", - "description": "Description", - "public": "Public", - "collaborative": "Collaborative", - "search_local_tracks": "Search local tracks...", - "play": "Play", - "delete": "Delete", - "none": "None", - "sort_a_z": "Sort by A-Z", - "sort_z_a": "Sort by Z-A", - "sort_artist": "Sort by Artist", - "sort_album": "Sort by Album", - "sort_duration": "Sort by Duration", - "sort_tracks": "Sort Tracks", - "currently_downloading": "Currently Downloading ({tracks_length})", - "cancel_all": "Cancel All", - "filter_artist": "Filter artists...", - "followers": "{followers} Followers", - "add_artist_to_blacklist": "Add artist to blacklist", - "top_tracks": "Top Tracks", - "fans_also_like": "Fans also like", - "loading": "Loading...", - "artist": "Artist", - "blacklisted": "Blacklisted", - "following": "Following", - "follow": "Follow", - "artist_url_copied": "Artist URL copied to clipboard", - "added_to_queue": "Added {tracks} tracks to queue", - "filter_albums": "Filter albums...", - "synced": "Synced", - "plain": "Plain", - "shuffle": "Shuffle", - "search_tracks": "Search tracks...", - "released": "Released", - "error": "Error {error}", - "title": "Title", - "time": "Time", - "more_actions": "More actions", - "download_count": "Download ({count})", - "add_count_to_playlist": "Add ({count}) to Playlist", - "add_count_to_queue": "Add ({count}) to Queue", - "play_count_next": "Play ({count}) next", - "album": "Album", - "copied_to_clipboard": "Copied {data} to clipboard", - "add_to_following_playlists": "Add {track} to following Playlists", - "add": "Add", - "added_track_to_queue": "Added {track} to queue", - "add_to_queue": "Add to queue", - "track_will_play_next": "{track} will play next", - "play_next": "Play next", - "removed_track_from_queue": "Removed {track} from queue", - "remove_from_queue": "Remove from queue", - "remove_from_favorites": "Remove from favorites", - "save_as_favorite": "Save as favorite", - "add_to_playlist": "Add to playlist", - "remove_from_playlist": "Remove from playlist", - "add_to_blacklist": "Add to blacklist", - "remove_from_blacklist": "Remove from blacklist", - "share": "Share", - "mini_player": "Mini Player", - "slide_to_seek": "Slide to seek forward or backward", - "shuffle_playlist": "Shuffle playlist", - "unshuffle_playlist": "Unshuffle playlist", - "previous_track": "Previous track", - "next_track": "Next track", - "pause_playback": "Pause Playback", - "resume_playback": "Resume Playback", - "loop_track": "Loop track", - "no_loop": "No loop", - "repeat_playlist": "Repeat playlist", - "queue": "Queue", - "alternative_track_sources": "Alternative track sources", - "download_track": "Download track", - "tracks_in_queue": "{tracks} tracks in queue", - "clear_all": "Clear all", - "show_hide_ui_on_hover": "Show/Hide UI on hover", - "always_on_top": "Always on top", - "exit_mini_player": "Exit Mini player", - "download_location": "Download location", - "local_library": "Local library", - "add_library_location": "Add to library", - "remove_library_location": "Remove from library", - "account": "Account", - "login_with_spotify": "Login with your Spotify account", - "connect_with_spotify": "Connect with Spotify", - "logout": "Logout", - "logout_of_this_account": "Logout of this account", - "language_region": "Language & Region", - "language": "Language", - "system_default": "System Default", - "market_place_region": "Marketplace Region", - "recommendation_country": "Recommendation Country", - "appearance": "Appearance", - "layout_mode": "Layout Mode", - "override_layout_settings": "Override responsive layout mode settings", - "adaptive": "Adaptive", - "compact": "Compact", - "extended": "Extended", - "theme": "Theme", - "dark": "Dark", - "light": "Light", - "system": "System", - "accent_color": "Accent Color", - "sync_album_color": "Sync album color", - "sync_album_color_description": "Uses the dominant color of the album art as the accent color", - "playback": "Playback", - "audio_quality": "Audio Quality", - "high": "High", - "low": "Low", - "pre_download_play": "Pre-download and play", - "pre_download_play_description": "Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)", - "skip_non_music": "Skip non-music segments (SponsorBlock)", - "blacklist_description": "Blacklisted tracks and artists", - "wait_for_download_to_finish": "Please wait for the current download to finish", - "desktop": "Desktop", - "close_behavior": "Close Behavior", - "close": "Close", - "minimize_to_tray": "Minimize to tray", - "show_tray_icon": "Show System tray icon", - "about": "About", - "u_love_spotube": "We know you love Spotube", - "check_for_updates": "Check for updates", - "about_spotube": "About Spotube", - "blacklist": "Blacklist", - "please_sponsor": "Please Sponsor/Donate", - "spotube_description": "Spotube, a lightweight, cross-platform, free-for-all spotify client", - "version": "Version", - "build_number": "Build Number", - "founder": "Founder", - "repository": "Repository", - "bug_issues": "Bug+Issues", - "made_with": "Made with ❤️ in Bangladesh🇧🇩", - "kingkor_roy_tirtho": "Kingkor Roy Tirtho", - "copyright": "© 2021-{current_year} Kingkor Roy Tirtho", - "license": "License", - "add_spotify_credentials": "Add your spotify credentials to get started", - "credentials_will_not_be_shared_disclaimer": "Don't worry, any of your credentials won't be collected or shared with anyone", - "know_how_to_login": "Don't know how to do this?", - "follow_step_by_step_guide": "Follow along the Step by Step guide", - "spotify_cookie": "Spotify {name} Cookie", - "cookie_name_cookie": "{name} Cookie", - "fill_in_all_fields": "Please fill in all the fields", - "submit": "Submit", - "exit": "Exit", - "previous": "Previous", - "next": "Next", - "done": "Done", - "step_1": "Step 1", - "first_go_to": "First, Go to", - "login_if_not_logged_in": "and Login/Signup if you are not logged in", - "step_2": "Step 2", - "step_2_steps": "1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection", - "step_3": "Step 3", - "step_3_steps": "Copy the value of \"sp_dc\" Cookie", - "success_emoji": "Success🥳", - "success_message": "Now you've successfully Logged in with your Spotify account. Good Job, mate!", - "step_4": "Step 4", - "step_4_steps": "Paste the copied \"sp_dc\" value", - "something_went_wrong": "Something went wrong", - "piped_instance": "Piped Server Instance", - "piped_description": "The Piped server instance to use for track matching", - "piped_warning": "Some of them might not work well. So use at your own risk", - "invidious_instance": "Invidious Server Instance", - "invidious_description": "The Invidious server instance to use for track matching", - "invidious_warning": "Some of them might not work well. So use at your own risk", - "generate": "Generate", - "track_exists": "Track {track} already exists", - "replace_downloaded_tracks": "Replace all downloaded tracks", - "skip_download_tracks": "Skip downloading all downloaded tracks", - "do_you_want_to_replace": "Do you want to replace the existing track??", - "replace": "Replace", - "skip": "Skip", - "select_up_to_count_type": "Select up to {count} {type}", - "select_genres": "Select Genres", - "add_genres": "Add Genres", - "country": "Country", - "number_of_tracks_generate": "Number of tracks to generate", - "acousticness": "Acousticness", - "danceability": "Danceability", - "energy": "Energy", - "instrumentalness": "Instrumentalness", - "liveness": "Liveness", - "loudness": "Loudness", - "speechiness": "Speechiness", - "valence": "Valence", - "popularity": "Popularity", - "key": "Key", - "duration": "Duration (s)", - "tempo": "Tempo (BPM)", - "mode": "Mode", - "time_signature": "Time Signature", - "short": "Short", - "medium": "Medium", - "long": "Long", - "min": "Min", - "max": "Max", - "target": "Target", - "moderate": "Moderate", - "deselect_all": "Deselect All", - "select_all": "Select All", - "are_you_sure": "Are you sure?", - "generating_playlist": "Generating your custom playlist...", - "selected_count_tracks": "Selected {count} tracks", - "download_warning": "If you download all Tracks at bulk you're clearly pirating Music & causing damage to the creative society of Music. I hope you are aware of this. Always, try respecting & supporting Artist's hard work", - "download_ip_ban_warning": "BTW, your IP can get blocked on YouTube due excessive download requests than usual. IP block means you can't use YouTube (even if you're logged in) for at least 2-3 months from that IP device. And Spotube doesn't hold any responsibility if this ever happens", - "by_clicking_accept_terms": "By clicking 'accept' you agree to following terms:", - "download_agreement_1": "I know I'm pirating Music. I'm bad", - "download_agreement_2": "I'll support the Artist wherever I can and I'm only doing this because I don't have money to buy their art", - "download_agreement_3": "I'm completely aware that my IP can get blocked on YouTube & I don't hold Spotube or his owners/contributors responsible for any accidents caused by my current action", - "decline": "Decline", - "accept": "Accept", - "details": "Details", - "youtube": "YouTube", - "channel": "Channel", - "likes": "Likes", - "dislikes": "Dislikes", - "views": "Views", - "streamUrl": "Stream URL", - "stop": "Stop", - "sort_newest": "Sort by newest added", - "sort_oldest": "Sort by oldest added", - "sleep_timer": "Sleep Timer", - "mins": "{minutes} Minutes", - "hours": "{hours} Hours", - "hour": "{hours} Hour", - "custom_hours": "Custom Hours", - "logs": "Logs", - "developers": "Developers", - "not_logged_in": "You're not logged in", - "search_mode": "Search Mode", - "audio_source": "Audio Source", - "ok": "Ok", - "failed_to_encrypt": "Failed to encrypt", - "encryption_failed_warning": "Spotube uses encryption to securely store your data. But failed to do so. So it'll fallback to insecure storage\nIf you're using linux, please make sure you've any secret-service (gnome-keyring, kde-wallet, keepassxc etc) installed", - "querying_info": "Querying info...", - "piped_api_down": "Piped API is down", - "piped_down_error_instructions": "The Piped instance {pipedInstance} is currently down\n\nEither change the instance or change the 'API type' to official YouTube API\n\nMake sure to restart the app after change", - "you_are_offline": "You are currently offline", - "connection_restored": "Your internet connection was restored", - "use_system_title_bar": "Use system title bar", - "crunching_results": "Crunching results...", - "search_to_get_results": "Search to get results", - "use_amoled_mode": "Pitch black dark theme", - "pitch_dark_theme": "AMOLED Mode", - "normalize_audio": "Normalize audio", - "change_cover": "Change cover", - "add_cover": "Add cover", - "restore_defaults": "Restore defaults", - "download_music_codec": "Download music codec", - "streaming_music_codec": "Streaming music codec", - "login_with_lastfm": "Login with Last.fm", - "connect": "Connect", - "disconnect_lastfm": "Disconnect Last.fm", - "disconnect": "Disconnect", - "username": "Username", - "password": "Password", - "login": "Login", - "login_with_your_lastfm": "Login with your Last.fm account", - "scrobble_to_lastfm": "Scrobble to Last.fm", - "go_to_album": "Go to Album", - "discord_rich_presence": "Discord Rich Presence", - "browse_all": "Browse All", - "genres": "Genres", - "explore_genres": "Explore Genres", - "friends": "Friends", - "no_lyrics_available": "Sorry, unable find lyrics for this track", - "start_a_radio": "Start a Radio", - "how_to_start_radio": "How do you want to start the radio?", - "replace_queue_question": "Do you want to replace the current queue or append to it?", - "endless_playback": "Endless Playback", - "delete_playlist": "Delete Playlist", - "delete_playlist_confirmation": "Are you sure you want to delete this playlist?", - "local_tracks": "Local Tracks", - "local_tab": "Local", - "song_link": "Song Link", - "skip_this_nonsense": "Skip this nonsense", - "freedom_of_music": "“Freedom of Music”", - "freedom_of_music_palm": "“Freedom of Music in the palm of your hand”", - "get_started": "Let's get started", - "youtube_source_description": "Recommended and works best.", - "piped_source_description": "Feeling free? Same as YouTube but a lot free.", - "jiosaavn_source_description": "Best for South Asian region.", - "invidious_source_description": "Similar to Piped but with higher availability.", - "highest_quality": "Highest Quality: {quality}", - "select_audio_source": "Select Audio Source", - "endless_playback_description": "Automatically append new songs\nto the end of the queue", - "choose_your_region": "Choose your region", - "choose_your_region_description": "This will help Spotube show you the right content\nfor your location.", - "choose_your_language": "Choose your language", - "help_project_grow": "Help this project grow", - "help_project_grow_description": "Spotube is an open-source project. You can help this project grow by contributing to the project, reporting bugs, or suggesting new features.", - "contribute_on_github": "Contribute on GitHub", - "donate_on_open_collective": "Donate on Open Collective", - "browse_anonymously": "Browse Anonymously", - "enable_connect": "Enable Connect", - "enable_connect_description": "Control Spotube from other devices", - "devices": "Devices", - "select": "Select", - "connect_client_alert": "You're being controlled by {client}", - "this_device": "This Device", - "remote": "Remote", - "stats": "Stats", - "and_n_more": "and {count} more", - "recently_played": "Recently Played", - "browse_more": "Browse More", - "no_title": "No Title", - "not_playing": "Not playing", - "epic_failure": "Epic failure!", - "added_num_tracks_to_queue": "Added {tracks_length} tracks to queue", - "spotube_has_an_update": "Spotube has an update", - "download_now": "Download Now", - "nightly_version": "Spotube Nightly {nightlyBuildNum} has been released", - "release_version": "Spotube v{version} has been released", - "read_the_latest": "Read the latest ", - "release_notes": "release notes", - "pick_color_scheme": "Pick color scheme", - "save": "Save", - "choose_the_device": "Choose the device:", - "multiple_device_connected": "There are multiple device connected.\nChoose the device you want this action to take place", - "nothing_found": "Nothing found", - "the_box_is_empty": "The box is empty", - "top_artists": "Top Artists", - "top_albums": "Top Albums", - "this_week": "This week", - "this_month": "This month", - "last_6_months": "Last 6 months", - "this_year": "This year", - "last_2_years": "Last 2 years", - "all_time": "All time", - "powered_by_provider": "Powered by {providerName}", - "email": "Email", - "profile_followers": "Followers", - "birthday": "Birthday", - "subscription": "Subscription", - "not_born": "Not born", - "hacker": "Hacker", - "profile": "Profile", - "no_name": "No Name", - "edit": "Edit", - "user_profile": "User Profile", - "count_plays": "{count} plays", - "streaming_fees_hypothetical": "Streaming fees (hypothetical)", - "minutes_listened": "Minutes listened", - "streamed_songs": "Streamed songs", - "count_streams": "{count} streams", - "owned_by_you": "Owned by you", - "copied_shareurl_to_clipboard": "Copied {shareUrl} to clipboard", - "spotify_hipotetical_calculation": "*This is calculated based on Spotify's per stream\npayout of $0.003 to $0.005. This is a hypothetical\ncalculation to give user insight about how much they\nwould have paid to the artists if they were to listen\ntheir song in Spotify.", - "count_mins": "{minutes} mins", - "summary_minutes": "minutes", - "summary_listened_to_music": "Listened to music", - "summary_songs": "songs", - "summary_streamed_overall": "Streamed overall", - "summary_owed_to_artists": "Owed to artists\nthis month", - "summary_artists": "artist's", - "summary_music_reached_you": "Music reached you", - "summary_full_albums": "full albums", - "summary_got_your_love": "Got your love", - "summary_playlists": "playlists", - "summary_were_on_repeat": "Were on repeat", - "total_money": "Total {money}", - "webview_not_found": "Webview not found", - "webview_not_found_description": "No webview runtime is installed in your device.\nIf it's installed make sure it's in the Environment PATH\n\nAfter installing, restart the app", - "unsupported_platform": "Unsupported platform", - "cache_music": "Cache music", - "open": "Open", - "cache_folder": "Cache folder", - "export": "Export", - "clear_cache": "Clear cache", - "clear_cache_confirmation": "Do you want to clear the cache?", - "export_cache_files": "Export Cached Files", - "found_n_files": "Found {count} files", - "export_cache_confirmation": "Do you want to export these files to", - "exported_n_out_of_m_files": "Exported {filesExported} out of {files} files", - "undo": "Undo", - "download_all": "Download all", - "add_all_to_playlist": "Add all to playlist", - "add_all_to_queue": "Add all to queue", - "play_all_next": "Play all next", - "pause": "Pause", - "view_all": "View all", - "no_tracks_added_yet": "Looks like you haven't added any tracks yet", - "no_tracks": "Looks like there are no tracks here", - "no_tracks_listened_yet": "Looks like you haven't listened to anything yet", - "not_following_artists": "You're not following any artists", - "no_favorite_albums_yet": "Looks like you haven't added any albums to your favorites yet", - "no_logs_found": "No logs found", - "youtube_engine": "YouTube Engine", - "youtube_engine_not_installed_title": "{engine} is not installed", - "youtube_engine_not_installed_message": "{engine} is not installed in your system.", - "youtube_engine_set_path": "Make sure it's available in the PATH variable or\nset the absolute path to the {engine} executable below", - "youtube_engine_unix_issue_message": "In macOS/Linux/unix like OS's, setting path on .zshrc/.bashrc/.bash_profile etc. won't work.\nYou need to set the path in the shell configuration file", - "download": "Download", - "file_not_found": "File not found" -} \ No newline at end of file + "guest": "Guest", + "browse": "Browse", + "search": "Search", + "library": "Library", + "lyrics": "Lyrics", + "settings": "Settings", + "genre_categories_filter": "Filter categories or genres...", + "genre": "Genre", + "personalized": "Personalized", + "featured": "Featured", + "new_releases": "New Releases", + "songs": "Songs", + "playing_track": "Playing {track}", + "queue_clear_alert": "This will clear the current queue. {track_length} tracks will be removed\nDo you want to continue?", + "load_more": "Load more", + "playlists": "Playlists", + "artists": "Artists", + "albums": "Albums", + "tracks": "Tracks", + "downloads": "Downloads", + "filter_playlists": "Filter your playlists...", + "liked_tracks": "Liked Tracks", + "liked_tracks_description": "All your liked tracks", + "playlist": "Playlist", + "create_a_playlist": "Create a playlist", + "update_playlist": "Update playlist", + "create": "Create", + "cancel": "Cancel", + "update": "Update", + "playlist_name": "Playlist Name", + "name_of_playlist": "Name of the playlist", + "description": "Description", + "public": "Public", + "collaborative": "Collaborative", + "search_local_tracks": "Search local tracks...", + "play": "Play", + "delete": "Delete", + "none": "None", + "sort_a_z": "Sort by A-Z", + "sort_z_a": "Sort by Z-A", + "sort_artist": "Sort by Artist", + "sort_album": "Sort by Album", + "sort_duration": "Sort by Duration", + "sort_tracks": "Sort Tracks", + "currently_downloading": "Currently Downloading ({tracks_length})", + "cancel_all": "Cancel All", + "filter_artist": "Filter artists...", + "followers": "{followers} Followers", + "add_artist_to_blacklist": "Add artist to blacklist", + "top_tracks": "Top Tracks", + "fans_also_like": "Fans also like", + "loading": "Loading...", + "artist": "Artist", + "blacklisted": "Blacklisted", + "following": "Following", + "follow": "Follow", + "artist_url_copied": "Artist URL copied to clipboard", + "added_to_queue": "Added {tracks} tracks to queue", + "filter_albums": "Filter albums...", + "synced": "Synced", + "plain": "Plain", + "shuffle": "Shuffle", + "search_tracks": "Search tracks...", + "released": "Released", + "error": "Error {error}", + "title": "Title", + "time": "Time", + "more_actions": "More actions", + "download_count": "Download ({count})", + "add_count_to_playlist": "Add ({count}) to Playlist", + "add_count_to_queue": "Add ({count}) to Queue", + "play_count_next": "Play ({count}) next", + "album": "Album", + "copied_to_clipboard": "Copied {data} to clipboard", + "add_to_following_playlists": "Add {track} to following Playlists", + "add": "Add", + "added_track_to_queue": "Added {track} to queue", + "add_to_queue": "Add to queue", + "track_will_play_next": "{track} will play next", + "play_next": "Play next", + "removed_track_from_queue": "Removed {track} from queue", + "remove_from_queue": "Remove from queue", + "remove_from_favorites": "Remove from favorites", + "save_as_favorite": "Save as favorite", + "add_to_playlist": "Add to playlist", + "remove_from_playlist": "Remove from playlist", + "add_to_blacklist": "Add to blacklist", + "remove_from_blacklist": "Remove from blacklist", + "share": "Share", + "mini_player": "Mini Player", + "slide_to_seek": "Slide to seek forward or backward", + "shuffle_playlist": "Shuffle playlist", + "unshuffle_playlist": "Unshuffle playlist", + "previous_track": "Previous track", + "next_track": "Next track", + "pause_playback": "Pause Playback", + "resume_playback": "Resume Playback", + "loop_track": "Loop track", + "no_loop": "No loop", + "repeat_playlist": "Repeat playlist", + "queue": "Queue", + "alternative_track_sources": "Alternative track sources", + "download_track": "Download track", + "tracks_in_queue": "{tracks} tracks in queue", + "clear_all": "Clear all", + "show_hide_ui_on_hover": "Show/Hide UI on hover", + "always_on_top": "Always on top", + "exit_mini_player": "Exit Mini player", + "download_location": "Download location", + "local_library": "Local library", + "add_library_location": "Add to library", + "remove_library_location": "Remove from library", + "account": "Account", + "login_with_spotify": "Login with your Spotify account", + "connect_with_spotify": "Connect with Spotify", + "logout": "Logout", + "logout_of_this_account": "Logout of this account", + "language_region": "Language & Region", + "language": "Language", + "system_default": "System Default", + "market_place_region": "Marketplace Region", + "recommendation_country": "Recommendation Country", + "appearance": "Appearance", + "layout_mode": "Layout Mode", + "override_layout_settings": "Override responsive layout mode settings", + "adaptive": "Adaptive", + "compact": "Compact", + "extended": "Extended", + "theme": "Theme", + "dark": "Dark", + "light": "Light", + "system": "System", + "accent_color": "Accent Color", + "sync_album_color": "Sync album color", + "sync_album_color_description": "Uses the dominant color of the album art as the accent color", + "playback": "Playback", + "audio_quality": "Audio Quality", + "high": "High", + "low": "Low", + "pre_download_play": "Pre-download and play", + "pre_download_play_description": "Instead of streaming audio, download bytes and play instead (Recommended for higher bandwidth users)", + "skip_non_music": "Skip non-music segments (SponsorBlock)", + "blacklist_description": "Blacklisted tracks and artists", + "wait_for_download_to_finish": "Please wait for the current download to finish", + "desktop": "Desktop", + "close_behavior": "Close Behavior", + "close": "Close", + "minimize_to_tray": "Minimize to tray", + "show_tray_icon": "Show System tray icon", + "about": "About", + "u_love_spotube": "We know you love Spotube", + "check_for_updates": "Check for updates", + "about_spotube": "About Spotube", + "blacklist": "Blacklist", + "please_sponsor": "Please Sponsor/Donate", + "spotube_description": "Spotube, a lightweight, cross-platform, free-for-all spotify client", + "version": "Version", + "build_number": "Build Number", + "founder": "Founder", + "repository": "Repository", + "bug_issues": "Bug+Issues", + "made_with": "Made with ❤️ in Bangladesh🇧🇩", + "kingkor_roy_tirtho": "Kingkor Roy Tirtho", + "copyright": "© 2021-{current_year} Kingkor Roy Tirtho", + "license": "License", + "add_spotify_credentials": "Add your spotify credentials to get started", + "credentials_will_not_be_shared_disclaimer": "Don't worry, any of your credentials won't be collected or shared with anyone", + "know_how_to_login": "Don't know how to do this?", + "follow_step_by_step_guide": "Follow along the Step by Step guide", + "spotify_cookie": "Spotify {name} Cookie", + "cookie_name_cookie": "{name} Cookie", + "fill_in_all_fields": "Please fill in all the fields", + "submit": "Submit", + "exit": "Exit", + "previous": "Previous", + "next": "Next", + "done": "Done", + "step_1": "Step 1", + "first_go_to": "First, Go to", + "login_if_not_logged_in": "and Login/Signup if you are not logged in", + "step_2": "Step 2", + "step_2_steps": "1. Once you're logged in, press F12 or Mouse Right Click > Inspect to Open the Browser devtools.\n2. Then go the \"Application\" Tab (Chrome, Edge, Brave etc..) or \"Storage\" Tab (Firefox, Palemoon etc..)\n3. Go to the \"Cookies\" section then the \"https://accounts.spotify.com\" subsection", + "step_3": "Step 3", + "step_3_steps": "Copy the value of \"sp_dc\" Cookie", + "success_emoji": "Success🥳", + "success_message": "Now you've successfully Logged in with your Spotify account. Good Job, mate!", + "step_4": "Step 4", + "step_4_steps": "Paste the copied \"sp_dc\" value", + "something_went_wrong": "Something went wrong", + "piped_instance": "Piped Server Instance", + "piped_description": "The Piped server instance to use for track matching", + "piped_warning": "Some of them might not work well. So use at your own risk", + "invidious_instance": "Invidious Server Instance", + "invidious_description": "The Invidious server instance to use for track matching", + "invidious_warning": "Some of them might not work well. So use at your own risk", + "generate": "Generate", + "track_exists": "Track {track} already exists", + "replace_downloaded_tracks": "Replace all downloaded tracks", + "skip_download_tracks": "Skip downloading all downloaded tracks", + "do_you_want_to_replace": "Do you want to replace the existing track??", + "replace": "Replace", + "skip": "Skip", + "select_up_to_count_type": "Select up to {count} {type}", + "select_genres": "Select Genres", + "add_genres": "Add Genres", + "country": "Country", + "number_of_tracks_generate": "Number of tracks to generate", + "acousticness": "Acousticness", + "danceability": "Danceability", + "energy": "Energy", + "instrumentalness": "Instrumentalness", + "liveness": "Liveness", + "loudness": "Loudness", + "speechiness": "Speechiness", + "valence": "Valence", + "popularity": "Popularity", + "key": "Key", + "duration": "Duration (s)", + "tempo": "Tempo (BPM)", + "mode": "Mode", + "time_signature": "Time Signature", + "short": "Short", + "medium": "Medium", + "long": "Long", + "min": "Min", + "max": "Max", + "target": "Target", + "moderate": "Moderate", + "deselect_all": "Deselect All", + "select_all": "Select All", + "are_you_sure": "Are you sure?", + "generating_playlist": "Generating your custom playlist...", + "selected_count_tracks": "Selected {count} tracks", + "download_warning": "If you download all Tracks at bulk you're clearly pirating Music & causing damage to the creative society of Music. I hope you are aware of this. Always, try respecting & supporting Artist's hard work", + "download_ip_ban_warning": "BTW, your IP can get blocked on YouTube due excessive download requests than usual. IP block means you can't use YouTube (even if you're logged in) for at least 2-3 months from that IP device. And Spotube doesn't hold any responsibility if this ever happens", + "by_clicking_accept_terms": "By clicking 'accept' you agree to following terms:", + "download_agreement_1": "I know I'm pirating Music. I'm bad", + "download_agreement_2": "I'll support the Artist wherever I can and I'm only doing this because I don't have money to buy their art", + "download_agreement_3": "I'm completely aware that my IP can get blocked on YouTube & I don't hold Spotube or his owners/contributors responsible for any accidents caused by my current action", + "decline": "Decline", + "accept": "Accept", + "details": "Details", + "youtube": "YouTube", + "channel": "Channel", + "likes": "Likes", + "dislikes": "Dislikes", + "views": "Views", + "streamUrl": "Stream URL", + "stop": "Stop", + "sort_newest": "Sort by newest added", + "sort_oldest": "Sort by oldest added", + "sleep_timer": "Sleep Timer", + "mins": "{minutes} Minutes", + "hours": "{hours} Hours", + "hour": "{hours} Hour", + "custom_hours": "Custom Hours", + "logs": "Logs", + "developers": "Developers", + "not_logged_in": "You're not logged in", + "search_mode": "Search Mode", + "audio_source": "Audio Source", + "ok": "Ok", + "failed_to_encrypt": "Failed to encrypt", + "encryption_failed_warning": "Spotube uses encryption to securely store your data. But failed to do so. So it'll fallback to insecure storage\nIf you're using linux, please make sure you've any secret-service (gnome-keyring, kde-wallet, keepassxc etc) installed", + "querying_info": "Querying info...", + "piped_api_down": "Piped API is down", + "piped_down_error_instructions": "The Piped instance {pipedInstance} is currently down\n\nEither change the instance or change the 'API type' to official YouTube API\n\nMake sure to restart the app after change", + "you_are_offline": "You are currently offline", + "connection_restored": "Your internet connection was restored", + "use_system_title_bar": "Use system title bar", + "crunching_results": "Crunching results...", + "search_to_get_results": "Search to get results", + "use_amoled_mode": "Pitch black dark theme", + "pitch_dark_theme": "AMOLED Mode", + "normalize_audio": "Normalize audio", + "change_cover": "Change cover", + "add_cover": "Add cover", + "restore_defaults": "Restore defaults", + "download_music_codec": "Download music codec", + "streaming_music_codec": "Streaming music codec", + "login_with_lastfm": "Login with Last.fm", + "connect": "Connect", + "disconnect_lastfm": "Disconnect Last.fm", + "disconnect": "Disconnect", + "username": "Username", + "password": "Password", + "login": "Login", + "login_with_your_lastfm": "Login with your Last.fm account", + "scrobble_to_lastfm": "Scrobble to Last.fm", + "go_to_album": "Go to Album", + "discord_rich_presence": "Discord Rich Presence", + "browse_all": "Browse All", + "genres": "Genres", + "explore_genres": "Explore Genres", + "friends": "Friends", + "no_lyrics_available": "Sorry, unable find lyrics for this track", + "start_a_radio": "Start a Radio", + "how_to_start_radio": "How do you want to start the radio?", + "replace_queue_question": "Do you want to replace the current queue or append to it?", + "endless_playback": "Endless Playback", + "delete_playlist": "Delete Playlist", + "delete_playlist_confirmation": "Are you sure you want to delete this playlist?", + "local_tracks": "Local Tracks", + "local_tab": "Local", + "song_link": "Song Link", + "skip_this_nonsense": "Skip this nonsense", + "freedom_of_music": "“Freedom of Music”", + "freedom_of_music_palm": "“Freedom of Music in the palm of your hand”", + "get_started": "Let's get started", + "youtube_source_description": "Recommended and works best.", + "piped_source_description": "Feeling free? Same as YouTube but a lot free.", + "jiosaavn_source_description": "Best for South Asian region.", + "invidious_source_description": "Similar to Piped but with higher availability.", + "highest_quality": "Highest Quality: {quality}", + "select_audio_source": "Select Audio Source", + "endless_playback_description": "Automatically append new songs\nto the end of the queue", + "choose_your_region": "Choose your region", + "choose_your_region_description": "This will help Spotube show you the right content\nfor your location.", + "choose_your_language": "Choose your language", + "help_project_grow": "Help this project grow", + "help_project_grow_description": "Spotube is an open-source project. You can help this project grow by contributing to the project, reporting bugs, or suggesting new features.", + "contribute_on_github": "Contribute on GitHub", + "donate_on_open_collective": "Donate on Open Collective", + "browse_anonymously": "Browse Anonymously", + "enable_connect": "Enable Connect", + "enable_connect_description": "Control Spotube from other devices", + "devices": "Devices", + "select": "Select", + "connect_client_alert": "You're being controlled by {client}", + "this_device": "This Device", + "remote": "Remote", + "stats": "Stats", + "and_n_more": "and {count} more", + "recently_played": "Recently Played", + "browse_more": "Browse More", + "no_title": "No Title", + "not_playing": "Not playing", + "epic_failure": "Epic failure!", + "added_num_tracks_to_queue": "Added {tracks_length} tracks to queue", + "spotube_has_an_update": "Spotube has an update", + "download_now": "Download Now", + "nightly_version": "Spotube Nightly {nightlyBuildNum} has been released", + "release_version": "Spotube v{version} has been released", + "read_the_latest": "Read the latest ", + "release_notes": "release notes", + "pick_color_scheme": "Pick color scheme", + "save": "Save", + "choose_the_device": "Choose the device:", + "multiple_device_connected": "There are multiple device connected.\nChoose the device you want this action to take place", + "nothing_found": "Nothing found", + "the_box_is_empty": "The box is empty", + "top_artists": "Top Artists", + "top_albums": "Top Albums", + "this_week": "This week", + "this_month": "This month", + "last_6_months": "Last 6 months", + "this_year": "This year", + "last_2_years": "Last 2 years", + "all_time": "All time", + "powered_by_provider": "Powered by {providerName}", + "email": "Email", + "profile_followers": "Followers", + "birthday": "Birthday", + "subscription": "Subscription", + "not_born": "Not born", + "hacker": "Hacker", + "profile": "Profile", + "no_name": "No Name", + "edit": "Edit", + "user_profile": "User Profile", + "count_plays": "{count} plays", + "streaming_fees_hypothetical": "Streaming fees (hypothetical)", + "minutes_listened": "Minutes listened", + "streamed_songs": "Streamed songs", + "count_streams": "{count} streams", + "owned_by_you": "Owned by you", + "copied_shareurl_to_clipboard": "Copied {shareUrl} to clipboard", + "spotify_hipotetical_calculation": "*This is calculated based on Spotify's per stream\npayout of $0.003 to $0.005. This is a hypothetical\ncalculation to give user insight about how much they\nwould have paid to the artists if they were to listen\ntheir song in Spotify.", + "count_mins": "{minutes} mins", + "summary_minutes": "minutes", + "summary_listened_to_music": "Listened to music", + "summary_songs": "songs", + "summary_streamed_overall": "Streamed overall", + "summary_owed_to_artists": "Owed to artists\nthis month", + "summary_artists": "artist's", + "summary_music_reached_you": "Music reached you", + "summary_full_albums": "full albums", + "summary_got_your_love": "Got your love", + "summary_playlists": "playlists", + "summary_were_on_repeat": "Were on repeat", + "total_money": "Total {money}", + "webview_not_found": "Webview not found", + "webview_not_found_description": "No webview runtime is installed in your device.\nIf it's installed make sure it's in the Environment PATH\n\nAfter installing, restart the app", + "unsupported_platform": "Unsupported platform", + "cache_music": "Cache music", + "open": "Open", + "cache_folder": "Cache folder", + "export": "Export", + "clear_cache": "Clear cache", + "clear_cache_confirmation": "Do you want to clear the cache?", + "export_cache_files": "Export Cached Files", + "found_n_files": "Found {count} files", + "export_cache_confirmation": "Do you want to export these files to", + "exported_n_out_of_m_files": "Exported {filesExported} out of {files} files", + "undo": "Undo", + "download_all": "Download all", + "add_all_to_playlist": "Add all to playlist", + "add_all_to_queue": "Add all to queue", + "play_all_next": "Play all next", + "pause": "Pause", + "view_all": "View all", + "no_tracks_added_yet": "Looks like you haven't added any tracks yet", + "no_tracks": "Looks like there are no tracks here", + "no_tracks_listened_yet": "Looks like you haven't listened to anything yet", + "not_following_artists": "You're not following any artists", + "no_favorite_albums_yet": "Looks like you haven't added any albums to your favorites yet", + "no_logs_found": "No logs found", + "youtube_engine": "YouTube Engine", + "youtube_engine_not_installed_title": "{engine} is not installed", + "youtube_engine_not_installed_message": "{engine} is not installed in your system.", + "youtube_engine_set_path": "Make sure it's available in the PATH variable or\nset the absolute path to the {engine} executable below", + "youtube_engine_unix_issue_message": "In macOS/Linux/unix like OS's, setting path on .zshrc/.bashrc/.bash_profile etc. won't work.\nYou need to set the path in the shell configuration file", + "download": "Download", + "file_not_found": "File not found", + "custom": "Custom", + "add_custom_url": "Add custom URL" +} diff --git a/lib/pages/settings/sections/playback.dart b/lib/pages/settings/sections/playback.dart index 8cbf7054..f3b7d131 100644 --- a/lib/pages/settings/sections/playback.dart +++ b/lib/pages/settings/sections/playback.dart @@ -4,6 +4,9 @@ import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' show ListTile; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -11,6 +14,8 @@ import 'package:piped_client/piped_client.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/components/form/text_form_field.dart'; +import 'package:spotube/hooks/controllers/use_shadcn_text_editing_controller.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/modules/settings/section_card_with_heading.dart'; import 'package:spotube/components/adaptive/adaptive_select_tile.dart'; @@ -97,32 +102,127 @@ class SettingsPlaybackSection extends HookConsumerWidget { ), value: preferences.pipedInstance, showValueWhenUnfolded: false, - options: data - .sortedBy((e) => e.name) - .map( - (e) => SelectItemButton( - value: e.apiUrl, - child: RichText( - text: TextSpan( - style: theme.typography.normal.copyWith( - color: theme.colorScheme.foreground, - ), - children: [ - TextSpan( - text: "${e.name.trim()}\n", - ), - TextSpan( - text: e.locations - .map(countryCodeToEmoji) - .join(""), - style: GoogleFonts.notoColorEmoji(), - ), - ], + trailing: [ + Tooltip( + tooltip: TooltipContainer( + child: Text(context.l10n.add_custom_url), + ), + child: IconButton.outline( + icon: const Icon(SpotubeIcons.edit), + size: ButtonSize.small, + onPressed: () { + showDialog( + context: context, + barrierColor: Colors.black.withValues(alpha: 0.5), + builder: (context) => HookBuilder( + builder: (context) { + final controller = + useShadcnTextEditingController( + text: preferences.pipedInstance, + ); + final formKey = useMemoized( + () => GlobalKey(), []); + + return Alert( + title: + Text(context.l10n.piped_instance).h4(), + content: FormBuilder( + key: formKey, + child: Column( + children: [ + const Gap(10), + TextFormBuilderField( + name: "url", + controller: controller, + placeholder: Text( + context.l10n.piped_instance), + validator: + FormBuilderValidators.url(), + ), + const Gap(10), + Row( + children: [ + Expanded( + child: Button.secondary( + onPressed: () { + Navigator.of(context).pop(); + }, + child: + Text(context.l10n.cancel), + ), + ), + const Gap(10), + Expanded( + child: Button.primary( + onPressed: () { + if (!formKey.currentState! + .saveAndValidate()) { + return; + } + preferencesNotifier + .setPipedInstance( + controller.text, + ); + Navigator.of(context).pop(); + }, + child: + Text(context.l10n.save), + ), + ), + ], + ) + ], + ), + ), + ); + }, ), + ); + }, + ), + ) + ], + options: [ + if (data + .none((e) => e.apiUrl == preferences.pipedInstance)) + SelectItemButton( + value: preferences.pipedInstance, + child: Text.rich( + TextSpan( + style: theme.typography.xSmall.copyWith( + color: theme.colorScheme.foreground, + ), + children: [ + TextSpan(text: context.l10n.custom), + const TextSpan(text: "\n"), + TextSpan(text: preferences.pipedInstance), + ], ), ), - ) - .toList(), + ), + for (final e in data.sortedBy((e) => e.name)) + SelectItemButton( + value: e.apiUrl, + child: RichText( + text: TextSpan( + style: theme.typography.normal.copyWith( + color: theme.colorScheme.foreground, + ), + children: [ + TextSpan( + text: "${e.name.trim()}\n", + ), + TextSpan( + text: e.locations + .map(countryCodeToEmoji) + .join(""), + style: GoogleFonts.notoColorEmoji(), + ), + ], + ), + ), + ), + ], onChanged: (value) { if (value != null) { preferencesNotifier.setPipedInstance(value); @@ -157,34 +257,129 @@ class SettingsPlaybackSection extends HookConsumerWidget { "${context.l10n.invidious_description}\n" "${context.l10n.invidious_warning}", ), + trailing: [ + Tooltip( + tooltip: TooltipContainer( + child: Text(context.l10n.add_custom_url), + ), + child: IconButton.outline( + icon: const Icon(SpotubeIcons.edit), + size: ButtonSize.small, + onPressed: () { + showDialog( + context: context, + barrierColor: Colors.black.withValues(alpha: 0.5), + builder: (context) => HookBuilder( + builder: (context) { + final controller = + useShadcnTextEditingController( + text: preferences.invidiousInstance, + ); + final formKey = useMemoized( + () => GlobalKey(), []); + + return Alert( + title: Text(context.l10n.invidious_instance) + .h4(), + content: FormBuilder( + key: formKey, + child: Column( + children: [ + const Gap(10), + TextFormBuilderField( + name: "url", + controller: controller, + placeholder: Text(context + .l10n.invidious_instance), + validator: + FormBuilderValidators.url(), + ), + const Gap(10), + Row( + children: [ + Expanded( + child: Button.secondary( + onPressed: () { + Navigator.of(context).pop(); + }, + child: + Text(context.l10n.cancel), + ), + ), + const Gap(10), + Expanded( + child: Button.primary( + onPressed: () { + if (!formKey.currentState! + .saveAndValidate()) { + return; + } + preferencesNotifier + .setInvidiousInstance( + controller.text, + ); + Navigator.of(context).pop(); + }, + child: + Text(context.l10n.save), + ), + ), + ], + ) + ], + ), + ), + ); + }, + ), + ); + }, + ), + ) + ], value: preferences.invidiousInstance, showValueWhenUnfolded: false, - options: data - .sortedBy((e) => e.name) - .map( - (e) => SelectItemButton( - value: e.details.uri, - child: RichText( - text: TextSpan( - style: theme.typography.normal.copyWith( - color: theme.colorScheme.foreground, - ), - children: [ - TextSpan( - text: "${e.name.trim()}\n", - ), - TextSpan( - text: countryCodeToEmoji( - e.details.region, - ), - style: GoogleFonts.notoColorEmoji(), - ), - ], + options: [ + if (data.none((e) => + e.details.uri == preferences.invidiousInstance)) + SelectItemButton( + value: preferences.invidiousInstance, + child: Text.rich( + TextSpan( + style: theme.typography.xSmall.copyWith( + color: theme.colorScheme.foreground, ), + children: [ + TextSpan(text: context.l10n.custom), + const TextSpan(text: "\n"), + TextSpan(text: preferences.invidiousInstance), + ], ), ), - ) - .toList(), + ), + for (final e in data.sortedBy((e) => e.name)) + SelectItemButton( + value: e.details.uri, + child: RichText( + text: TextSpan( + style: theme.typography.normal.copyWith( + color: theme.colorScheme.foreground, + ), + children: [ + TextSpan( + text: "${e.name.trim()}\n", + ), + TextSpan( + text: countryCodeToEmoji( + e.details.region, + ), + style: GoogleFonts.notoColorEmoji(), + ), + ], + ), + ), + ), + ], onChanged: (value) { if (value != null) { preferencesNotifier.setInvidiousInstance(value); diff --git a/untranslated_messages.json b/untranslated_messages.json index 0e65d0d3..44adb37f 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -22,7 +22,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "bn": [ @@ -48,7 +50,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ca": [ @@ -74,7 +78,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "cs": [ @@ -100,7 +106,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "de": [ @@ -126,7 +134,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "es": [ @@ -152,7 +162,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "eu": [ @@ -178,7 +190,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "fa": [ @@ -204,7 +218,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "fi": [ @@ -230,7 +246,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "fr": [ @@ -256,7 +274,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "hi": [ @@ -282,7 +302,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "id": [ @@ -308,7 +330,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "it": [ @@ -334,7 +358,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ja": [ @@ -360,7 +386,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ka": [ @@ -386,7 +414,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ko": [ @@ -412,7 +442,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ne": [ @@ -438,7 +470,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "nl": [ @@ -464,7 +498,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "pl": [ @@ -490,7 +526,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "pt": [ @@ -516,7 +554,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "ru": [ @@ -542,7 +582,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "th": [ @@ -568,7 +610,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "tr": [ @@ -594,7 +638,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "uk": [ @@ -620,7 +666,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "vi": [ @@ -646,7 +694,9 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ], "zh": [ @@ -672,6 +722,8 @@ "youtube_engine_set_path", "youtube_engine_unix_issue_message", "download", - "file_not_found" + "file_not_found", + "custom", + "add_custom_url" ] }