diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index fd80df96..ab2aa90a 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -586,7 +586,7 @@ class Settings : FragmentedStorageFileJson() { val activity = SettingsActivity.getActivity()!! if(!StateBackup.hasAutomaticBackup()) - UIDialogs.toast(activity, "You don't have any automatic backups", false); + UIDialogs.toast(activity, activity.getString(R.string.you_don_t_have_any_automatic_backups), false); else UIDialogs.showAutomaticRestoreDialog(activity, activity.lifecycleScope); } diff --git a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt index e6874619..39e3ad84 100644 --- a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt +++ b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt @@ -100,12 +100,12 @@ class UIDialogs { dialog.show(); }; if(StateBackup.hasAutomaticBackup() && !skipRestoreCheck) - UIDialogs.showDialog(context, R.drawable.ic_move_up, "An old backup is available", "Would you like to restore this backup?", null, 0, - UIDialogs.Action("Cancel", {}), //To nothing - UIDialogs.Action("Override", { + UIDialogs.showDialog(context, R.drawable.ic_move_up, context.getString(R.string.an_old_backup_is_available), context.getString(R.string.would_you_like_to_restore_this_backup), null, 0, + UIDialogs.Action(context.getString(R.string.cancel), {}), //To nothing + UIDialogs.Action(context.getString(R.string.override), { dialogAction(); }, UIDialogs.ActionStyle.DANGEROUS), - UIDialogs.Action("Restore", { + UIDialogs.Action(context.getString(R.string.restore), { UIDialogs.showAutomaticRestoreDialog(context, StateApp.instance.scope); }, UIDialogs.ActionStyle.PRIMARY)); else { @@ -211,10 +211,10 @@ class UIDialogs { (if(ex != null ) "${ex.message}" else ""), if(ex is PluginException) ex.code else null, 0, - UIDialogs.Action("Retry", { + UIDialogs.Action(context.getString(R.string.retry), { retryAction?.invoke(); }, UIDialogs.ActionStyle.PRIMARY), - UIDialogs.Action("Close", { + UIDialogs.Action(context.getString(R.string.close), { closeAction?.invoke() }, UIDialogs.ActionStyle.NONE) ); @@ -226,15 +226,15 @@ class UIDialogs { } fun showDataRetryDialog(context: Context, reason: String? = null, retryAction: (() -> Unit)? = null, closeAction: (() -> Unit)? = null) { - val retryButtonAction = Action("Retry", retryAction ?: {}, ActionStyle.PRIMARY) - val closeButtonAction = Action("Close", closeAction ?: {}, ActionStyle.ACCENT) - showDialog(context, R.drawable.ic_no_internet_86dp, "Data Retry", reason, null, 0, closeButtonAction, retryButtonAction) + val retryButtonAction = Action(context.getString(R.string.retry), retryAction ?: {}, ActionStyle.PRIMARY) + val closeButtonAction = Action(context.getString(R.string.close), closeAction ?: {}, ActionStyle.ACCENT) + showDialog(context, R.drawable.ic_no_internet_86dp, context.getString(R.string.data_retry), reason, null, 0, closeButtonAction, retryButtonAction) } fun showConfirmationDialog(context: Context, text: String, action: () -> Unit, cancelAction: (() -> Unit)? = null) { - val confirmButtonAction = Action("Confirm", action, ActionStyle.PRIMARY) - val cancelButtonAction = Action("Cancel", cancelAction ?: {}, ActionStyle.ACCENT) + val confirmButtonAction = Action(context.getString(R.string.confirm), action, ActionStyle.PRIMARY) + val cancelButtonAction = Action(context.getString(R.string.cancel), cancelAction ?: {}, ActionStyle.ACCENT) showDialog(context, R.drawable.ic_error, text, null, null, 0, cancelButtonAction, confirmButtonAction) } diff --git a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt index 754803f9..f1eb09eb 100644 --- a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt +++ b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt @@ -64,22 +64,22 @@ class UISlideOverlays { val subtitleSources = video.subtitles; if(videoSources.size == 0 && (audioSources?.size ?: 0) == 0) { - UIDialogs.toast("No downloads available", false); + UIDialogs.toast(container.context.getString(R.string.no_downloads_available), false); return null; } if(!VideoHelper.isDownloadable(video)) { Logger.i(TAG, "Attempted to open downloads without valid sources for [${video.name}]: ${video.url}"); - UIDialogs.toast( "No downloadable sources (yet)"); + UIDialogs.toast( container.context.getString(R.string.no_downloadable_sources_yet)); return null; } - items.add(SlideUpMenuGroup(container.context, "Video", videoSources, - listOf(listOf(SlideUpMenuItem(container.context, R.drawable.ic_movie, "None", "Audio Only", "none", { + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.video), videoSources, + listOf(listOf(SlideUpMenuItem(container.context, R.drawable.ic_movie, container.context.getString(R.string.none), container.context.getString(R.string.audio_only), "none", { selectedVideo = null; menu?.selectOption(videoSources, "none"); if(selectedAudio != null || !requiresAudio) - menu?.setOk("Download"); + menu?.setOk(container.context.getString(R.string.download)); }, false)) + videoSources .filter { it.isDownloadable() } @@ -88,7 +88,7 @@ class UISlideOverlays { selectedVideo = it as IVideoUrlSource; menu?.selectOption(videoSources, it); if(selectedAudio != null || !requiresAudio) - menu?.setOk("Download"); + menu?.setOk(container.context.getString(R.string.download)); }, false) }).flatten().toList() )); @@ -100,13 +100,13 @@ class UISlideOverlays { audioSources?.let { audioSources -> - items.add(SlideUpMenuGroup(container.context, "Audio", audioSources, audioSources + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.audio), audioSources, audioSources .filter { VideoHelper.isDownloadable(it) } .map { SlideUpMenuItem(container.context, R.drawable.ic_music, it.name, "${it.bitrate}", it, { selectedAudio = it as IAudioUrlSource; menu?.selectOption(audioSources, it); - menu?.setOk("Download"); + menu?.setOk(container.context.getString(R.string.download)); }, false); })); val asources = audioSources; @@ -125,7 +125,7 @@ class UISlideOverlays { //ContentResolver is required for subtitles.. if(contentResolver != null) { - items.add(SlideUpMenuGroup(container.context, "Subtitles", subtitleSources, subtitleSources + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.subtitles), subtitleSources, subtitleSources .map { SlideUpMenuItem(container.context, R.drawable.ic_edit, it.name, "", it, { if (selectedSubtitle == it) { @@ -139,7 +139,7 @@ class UISlideOverlays { })); } - menu = SlideUpMenuOverlay(container.context, container, "Download Video", null, true, items); + menu = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.download_video), null, true, items); if(selectedVideo != null) { menu.selectOption(videoSources, selectedVideo); @@ -148,7 +148,7 @@ class UISlideOverlays { audioSources?.let { audioSources -> menu.selectOption(audioSources, selectedAudio); }; } if(selectedAudio != null || (!requiresAudio && selectedVideo != null)) { - menu.setOk("Download"); + menu.setOk(container.context.getString(R.string.download)); } menu.onOK.subscribe { @@ -185,7 +185,7 @@ class UISlideOverlays { } fun showDownloadVideoOverlay(video: IPlatformVideo, container: ViewGroup, useDetails: Boolean = false) { val handleUnknownDownload: ()->Unit = { - showUnknownVideoDownload("Video", container) { px, bitrate -> + showUnknownVideoDownload(container.context.getString(R.string.video), container) { px, bitrate -> StateDownloads.instance.download(video, px, bitrate) }; }; @@ -195,7 +195,7 @@ class UISlideOverlays { val scope = StateApp.instance.scopeOrNull; if(scope != null) { - val loader = showLoaderOverlay("Fetching video details", container); + val loader = showLoaderOverlay(container.context.getString(R.string.fetching_video_details), container); scope.launch(Dispatchers.IO) { try { val videoDetails = StatePlatform.instance.getContentDetails(video.url, false).await(); @@ -209,7 +209,7 @@ class UISlideOverlays { } catch(ex: Throwable) { withContext(Dispatchers.Main) { - UIDialogs.toast("Failed to fetch details for download"); + UIDialogs.toast(container.context.getString(R.string.failed_to_fetch_details_for_download)); handleUnknownDownload(); loader.hide(true); } @@ -220,7 +220,7 @@ class UISlideOverlays { } } fun showDownloadPlaylistOverlay(playlist: Playlist, container: ViewGroup) { - showUnknownVideoDownload("Video", container) { px, bitrate -> + showUnknownVideoDownload(container.context.getString(R.string.video), container) { px, bitrate -> StateDownloads.instance.download(playlist, px, bitrate); }; } @@ -232,7 +232,7 @@ class UISlideOverlays { var targetBitrate: Long = 0; val resolutions = listOf( - Triple("None", "None", -1), + Triple(container.context.getString(R.string.none), container.context.getString(R.string.none), -1), Triple("480P", "720x480", 720*480), Triple("720P", "1280x720", 1280*720), Triple("1080P", "1920x1080", 1920*1080), @@ -240,23 +240,23 @@ class UISlideOverlays { Triple("2160P", "3840x2160", 3840*2160) ); - items.add(SlideUpMenuGroup(container.context, "Target Resolution", "Video", resolutions.map { + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.target_resolution), "Video", resolutions.map { SlideUpMenuItem(container.context, R.drawable.ic_movie, it.first, it.second, it.third, { targetPxSize = it.third; menu?.selectOption("Video", it.third); }, false) })); - items.add(SlideUpMenuGroup(container.context, "Target Bitrate", "Bitrate", listOf( - SlideUpMenuItem(container.context, R.drawable.ic_movie, "Low Bitrate", "", 1, { + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.target_bitrate), "Bitrate", listOf( + SlideUpMenuItem(container.context, R.drawable.ic_movie, container.context.getString(R.string.low_bitrate), "", 1, { targetBitrate = 1; menu?.selectOption("Bitrate", 1); - menu?.setOk("Download"); + menu?.setOk(container.context.getString(R.string.download)); }, false), - SlideUpMenuItem(container.context, R.drawable.ic_movie, "High Bitrate", "", 9999999, { + SlideUpMenuItem(container.context, R.drawable.ic_movie, container.context.getString(R.string.high_bitrate), "", 9999999, { targetBitrate = 9999999; menu?.selectOption("Bitrate", 9999999); - menu?.setOk("Download"); + menu?.setOk(container.context.getString(R.string.download)); }, false) ))); @@ -277,12 +277,12 @@ class UISlideOverlays { if(Settings.instance.downloads.isHighBitrateDefault()) { targetBitrate = 9999999; menu.selectOption("Bitrate", 9999999); - menu.setOk("Download"); + menu.setOk(container.context.getString(R.string.download)); } else { targetBitrate = 1; menu.selectOption("Bitrate", 1); - menu.setOk("Download"); + menu.setOk(container.context.getString(R.string.download)); } menu.onOK.subscribe { @@ -310,8 +310,8 @@ class UISlideOverlays { if (lastUpdated != null) { items.add( - SlideUpMenuGroup(container.context, "Recently Used Playlist", "recentlyusedplaylist", - SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, lastUpdated.name, "${lastUpdated.videos.size} videos", "", + SlideUpMenuGroup(container.context, container.context.getString(R.string.recently_used_playlist), "recentlyusedplaylist", + SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, lastUpdated.name, "${lastUpdated.videos.size} " + container.context.getString(R.string.videos), "", { StatePlaylists.instance.addToPlaylist(lastUpdated.id, video); StateDownloads.instance.checkForOutdatedPlaylists(); @@ -322,23 +322,23 @@ class UISlideOverlays { val allPlaylists = StatePlaylists.instance.getPlaylists(); val queue = StatePlayer.instance.getQueue(); val watchLater = StatePlaylists.instance.getWatchLater(); - items.add(SlideUpMenuGroup(container.context, "Actions", "actions", + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.actions), "actions", (listOf( - SlideUpMenuItem(container.context, R.drawable.ic_download, "Download", "Download the video", "download", + SlideUpMenuItem(container.context, R.drawable.ic_download, container.context.getString(R.string.download), container.context.getString(R.string.download_the_video), container.context.getString(R.string.download), { showDownloadVideoOverlay(video, container, true); }, false)) + actions) )); items.add( - SlideUpMenuGroup(container.context, "Add To", "addto", - SlideUpMenuItem(container.context, R.drawable.ic_queue_add, "Add to Queue", "${queue.size} videos", "queue", + SlideUpMenuGroup(container.context, container.context.getString(R.string.add_to), "addto", + SlideUpMenuItem(container.context, R.drawable.ic_queue_add, container.context.getString(R.string.add_to_queue), "${queue.size} " + container.context.getString(R.string.videos), "queue", { StatePlayer.instance.addToQueue(video); }), - SlideUpMenuItem(container.context, R.drawable.ic_watchlist_add, "Add to " + StatePlayer.TYPE_WATCHLATER + "", "${watchLater.size} videos", "watch later", + SlideUpMenuItem(container.context, R.drawable.ic_watchlist_add, "${container.context.getString(R.string.add_to)} " + StatePlayer.TYPE_WATCHLATER + "", "${watchLater.size} " + container.context.getString(R.string.videos), "watch later", { StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(video)); }) )); val playlistItems = arrayListOf(); for (playlist in allPlaylists) { - playlistItems.add(SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, "Add to " + playlist.name + "", "${playlist.videos.size} videos", "", + playlistItems.add(SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, "${container.context.getString(R.string.add_to)} " + playlist.name + "", "${playlist.videos.size} " + container.context.getString(R.string.videos), "", { StatePlaylists.instance.addToPlaylist(playlist.id, video); StateDownloads.instance.checkForOutdatedPlaylists(); @@ -346,9 +346,9 @@ class UISlideOverlays { } if(playlistItems.size > 0) - items.add(SlideUpMenuGroup(container.context, "Playlists", "", playlistItems)); + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.playlists), "", playlistItems)); - return SlideUpMenuOverlay(container.context, container, "Video Options", null, true, items).apply { show() }; + return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.video_options), null, true, items).apply { show() }; } @@ -360,8 +360,8 @@ class UISlideOverlays { if (lastUpdated != null) { items.add( - SlideUpMenuGroup(container.context, "Recently Used Playlist", "recentlyusedplaylist", - SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, lastUpdated.name, "${lastUpdated.videos.size} videos", "", + SlideUpMenuGroup(container.context, container.context.getString(R.string.recently_used_playlist), "recentlyusedplaylist", + SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, lastUpdated.name, "${lastUpdated.videos.size} " + container.context.getString(R.string.videos), "", { StatePlaylists.instance.addToPlaylist(lastUpdated.id, video); StateDownloads.instance.checkForOutdatedPlaylists(); @@ -373,18 +373,18 @@ class UISlideOverlays { val queue = StatePlayer.instance.getQueue(); val watchLater = StatePlaylists.instance.getWatchLater(); items.add( - SlideUpMenuGroup(container.context, "Other", "other", - SlideUpMenuItem(container.context, R.drawable.ic_queue_add, "Queue", "${queue.size} videos", "queue", + SlideUpMenuGroup(container.context, container.context.getString(R.string.other), "other", + SlideUpMenuItem(container.context, R.drawable.ic_queue_add, container.context.getString(R.string.queue), "${queue.size} " + container.context.getString(R.string.videos), "queue", { StatePlayer.instance.addToQueue(video); }), - SlideUpMenuItem(container.context, R.drawable.ic_watchlist_add, StatePlayer.TYPE_WATCHLATER, "${watchLater.size} videos", "watch later", + SlideUpMenuItem(container.context, R.drawable.ic_watchlist_add, StatePlayer.TYPE_WATCHLATER, "${watchLater.size} " + container.context.getString(R.string.videos), "watch later", { StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(video)); }), - SlideUpMenuItem(container.context, R.drawable.ic_download, "Download", "Download the video", "download", + SlideUpMenuItem(container.context, R.drawable.ic_download, container.context.getString(R.string.download), container.context.getString(R.string.download_the_video), container.context.getString(R.string.download), { showDownloadVideoOverlay(video, container, true); }, false)) ); val playlistItems = arrayListOf(); for (playlist in allPlaylists) { - playlistItems.add(SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, playlist.name, "${playlist.videos.size} videos", "", + playlistItems.add(SlideUpMenuItem(container.context, R.drawable.ic_playlist_add, playlist.name, "${playlist.videos.size} " + container.context.getString(R.string.videos), "", { StatePlaylists.instance.addToPlaylist(playlist.id, video); StateDownloads.instance.checkForOutdatedPlaylists(); @@ -392,9 +392,9 @@ class UISlideOverlays { } if(playlistItems.size > 0) - items.add(SlideUpMenuGroup(container.context, "Playlists", "", playlistItems)); + items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.playlists), "", playlistItems)); - return SlideUpMenuOverlay(container.context, container, "Add to", null, true, items).apply { show() }; + return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.add_to), null, true, items).apply { show() }; } fun showFiltersOverlay(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List, filterValues: HashMap>): SlideUpMenuFilters { @@ -412,8 +412,8 @@ class UISlideOverlays { .map { btn -> SlideUpMenuItem(container.context, btn.iconResource, btn.text.text.toString(), "", "", { btn.handler?.invoke(btn); }, true) as View }.toTypedArray() ?: arrayOf(), - arrayOf(SlideUpMenuItem(container.context, R.drawable.ic_pin, "Change Pins", "Decide which buttons should be pinned", "", { - showOrderOverlay(container, "Select your pins in order", (visible + hidden).map { Pair(it.text.text.toString(), it.tagRef!!) }) { + arrayOf(SlideUpMenuItem(container.context, R.drawable.ic_pin, container.context.getString(R.string.change_pins), container.context.getString(R.string.decide_which_buttons_should_be_pinned), "", { + showOrderOverlay(container, container.context.getString(R.string.select_your_pins_in_order), (visible + hidden).map { Pair(it.text.text.toString(), it.tagRef!!) }) { val selected = it .map { x -> visible.find { it.tagRef == x } ?: hidden.find { it.tagRef == x } } .filter { it != null } @@ -425,7 +425,7 @@ class UISlideOverlays { }, false)) ).flatten().toTypedArray(); - return SlideUpMenuOverlay(container.context, container, "More Options", null, true, *views).apply { show() }; + return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.more_options), null, true, *views).apply { show() }; } fun showOrderOverlay(container: ViewGroup, title: String, options: List>, onOrdered: (List)->Unit) { @@ -433,7 +433,7 @@ class UISlideOverlays { var overlay: SlideUpMenuOverlay? = null; - overlay = SlideUpMenuOverlay(container.context, container, title, "Save", true, + overlay = SlideUpMenuOverlay(container.context, container, title, container.context.getString(R.string.save), true, options.map { SlideUpMenuItem(container.context, R.drawable.ic_move_up, it.first, "", it.second, { if(overlay!!.selectOption(null, it.second, true, true)) { if(!selection.contains(it.second)) diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt index e7443e4c..e3e7eff2 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt @@ -160,7 +160,7 @@ class CommentViewHolder : ViewHolder { val replies = comment.replyCount ?: 0; if (!readonly || replies > 0) { _buttonReplies.visibility = View.VISIBLE; - _buttonReplies.text.text = "$replies replies"; + _buttonReplies.text.text = "$replies " + itemView.context.getString(R.string.replies); } else { _buttonReplies.visibility = View.GONE; } diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt index 63348033..83cc2d95 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceView.kt @@ -38,7 +38,7 @@ class DisabledSourceView : LinearLayout { client.icon?.setImageView(_imageSource); _textSource.text = client.name; - _textSourceSubtitle.text = "Tap to open"; + _textSourceSubtitle.text = context.getString(R.string.tap_to_open); _buttonAdd.setOnClickListener { onAdd.emit(source) } _root.setOnClickListener { onClick.emit(); }; diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceViewHolder.kt index c721b553..ed55fe62 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/DisabledSourceViewHolder.kt @@ -38,7 +38,7 @@ class DisabledSourceViewHolder : ViewHolder { client.icon?.setImageView(_imageSource); _textSource.text = client.name; - _textSourceSubtitle.text = "Tap to open"; + _textSourceSubtitle.text = itemView.context.getString(R.string.tap_to_open); source = client; } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt index 62aa12dc..b6796e84 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/EnabledSourceViewHolder.kt @@ -57,7 +57,7 @@ class EnabledSourceViewHolder : ViewHolder { client.icon?.setImageView(_imageSource); _textSource.text = client.name; - _textSourceSubtitle.text = "Tap to open"; + _textSourceSubtitle.text = itemView.context.getString(R.string.tap_to_open); source = client } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/HistoryListViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/HistoryListViewHolder.kt index b990dcc7..7194c1e0 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/HistoryListViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/HistoryListViewHolder.kt @@ -89,7 +89,7 @@ class HistoryListViewHolder : ViewHolder { var metadata = ""; if (v.video.viewCount > 0) - metadata += "${v.video.viewCount.toHumanNumber()} views"; + metadata += "${v.video.viewCount.toHumanNumber()} " + itemView.context.getString(R.string.views); _textMetadata.text = metadata; diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt index 22e66dae..34173dd1 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/PreviewVideoView.kt @@ -181,15 +181,15 @@ open class PreviewVideoView : LinearLayout { var metadata = "" if (content is IPlatformVideo && content.viewCount > 0) { if(content.isLive) - metadata += "${content.viewCount.toHumanNumber()} watching • "; + metadata += "${content.viewCount.toHumanNumber()} ${context.getString(R.string.watching)} • "; else - metadata += "${content.viewCount.toHumanNumber()} views • "; + metadata += "${content.viewCount.toHumanNumber()} ${context.getString(R.string.views)} • "; } var timeMeta = ""; if(isPlanned) { val ago = content.datetime?.toHumanNowDiffString(true) ?: "" - timeMeta = "available in " + ago; + timeMeta = context.getString(R.string.available_in) + " ${ago}"; } else { val ago = content.datetime?.toHumanNowDiffString() ?: "" @@ -210,13 +210,13 @@ open class PreviewVideoView : LinearLayout { if(!isPlanned) _textVideoDuration.text = video.duration.toHumanTime(false); else - _textVideoDuration.text = "Planned"; + _textVideoDuration.text = context.getString(R.string.planned); _playerVideoThumbnail?.setLive(video.isLive); if(!isPlanned && video.isLive) { _containerDuration.visibility = GONE; _containerLive.visibility = VISIBLE; - timeMeta = "LIVE" + timeMeta = context.getString(R.string.live_capitalized) } else { _containerLive.visibility = GONE; diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/VideoListEditorViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/VideoListEditorViewHolder.kt index 1089e471..8efb3cc9 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/VideoListEditorViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/VideoListEditorViewHolder.kt @@ -103,7 +103,7 @@ class VideoListEditorViewHolder : ViewHolder { var metadata = ""; if (v.viewCount > 0) - metadata += "${v.viewCount.toHumanNumber()} views • "; + metadata += "${v.viewCount.toHumanNumber()} ${itemView.context.getString(R.string.views)} • "; metadata += v.datetime?.toHumanNowDiffString() ?: ""; _platformIndicator.setPlatformFromClientID(v.id.pluginId); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/CreatorViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/CreatorViewHolder.kt index 9f75d99b..487ac8e7 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/CreatorViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/CreatorViewHolder.kt @@ -77,7 +77,7 @@ class CreatorViewHolder(private val _viewGroup: ViewGroup, private val _tiny: Bo if(authorLink.subscribers == null || (authorLink.subscribers ?: 0) <= 0L) _textMetadata.visibility = View.GONE; else { - _textMetadata.text = if((authorLink.subscribers ?: 0) > 0) authorLink.subscribers!!.toHumanNumber() + " subscribers" else ""; + _textMetadata.text = if((authorLink.subscribers ?: 0) > 0) authorLink.subscribers!!.toHumanNumber() + " " + _view.context.getString(R.string.subscribers) else ""; _textMetadata.visibility = View.VISIBLE; } _buttonSubscribe.setSubscribeChannel(authorLink.url); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ImportPlaylistsViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ImportPlaylistsViewHolder.kt index 669f987e..dc54dd3f 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ImportPlaylistsViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/ImportPlaylistsViewHolder.kt @@ -45,7 +45,7 @@ class ImportPlaylistsViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter. override fun bind(playlist: SelectablePlaylist) { _textName.text = playlist.playlist.name; - _textMetadata.text = "${playlist.playlist.videos.size} videos"; + _textMetadata.text = "${playlist.playlist.videos.size} " + _view.context.getString(R.string.videos); _checkbox.value = playlist.selected; val thumbnail = playlist.playlist.videos.firstOrNull()?.thumbnails?.getHQThumbnail(); diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/VideoDownloadViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/VideoDownloadViewHolder.kt index e97434b3..3fd9a3cd 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/VideoDownloadViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/viewholders/VideoDownloadViewHolder.kt @@ -37,7 +37,7 @@ class VideoDownloadViewHolder(_viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder< }; _videoDelete.setOnClickListener { val id = _video?.id ?: return@setOnClickListener; - UIDialogs.showConfirmationDialog(_view.context, "Are you sure you want to delete this video?", { + UIDialogs.showConfirmationDialog(_view.context, _view.context.getString(R.string.are_you_sure_you_want_to_delete_this_video), { StateDownloads.instance.deleteCachedVideo(id); }); } diff --git a/app/src/main/java/com/futo/platformplayer/views/announcements/AnnouncementView.kt b/app/src/main/java/com/futo/platformplayer/views/announcements/AnnouncementView.kt index 93c27e9a..d1b75a5b 100644 --- a/app/src/main/java/com/futo/platformplayer/views/announcements/AnnouncementView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/announcements/AnnouncementView.kt @@ -129,10 +129,10 @@ class AnnouncementView : LinearLayout { _textClose.text = announcement.cancelName; } else - _textClose.text = "Dismiss"; + _textClose.text = context.getString(R.string.dismiss); } else - _textClose.text = "Dismiss"; + _textClose.text = context.getString(R.string.dismiss); when (announcement.announceType) { AnnouncementType.DELETABLE -> { diff --git a/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt b/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt index 6b5c63f7..1b577311 100644 --- a/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt @@ -408,10 +408,10 @@ class GestureControlView : LinearLayout { val seekOffset: Long = 10000; if (_rewinding) { - _textRewind.text = "${_fastForwardCounter * 10} seconds"; + _textRewind.text = "${_fastForwardCounter * 10} " + context.getString(R.string.seconds); onSeek.emit(-seekOffset); } else { - _textFastForward.text = "${_fastForwardCounter * 10} seconds"; + _textFastForward.text = "${_fastForwardCounter * 10} " + context.getString(R.string.seconds); onSeek.emit(seekOffset); } } diff --git a/app/src/main/java/com/futo/platformplayer/views/comments/AddCommentView.kt b/app/src/main/java/com/futo/platformplayer/views/comments/AddCommentView.kt index a364a47e..276b02e8 100644 --- a/app/src/main/java/com/futo/platformplayer/views/comments/AddCommentView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/comments/AddCommentView.kt @@ -31,12 +31,12 @@ class AddCommentView : LinearLayout { val now = System.currentTimeMillis() if (now - _lastClickTime > 3000) { - StatePolycentric.instance.requireLogin(context, "Please login to post a comment") { + StatePolycentric.instance.requireLogin(context, context.getString(R.string.please_login_to_post_a_comment)) { try { UIDialogs.showCommentDialog(context, cu, ref) { onCommentAdded.emit(it) }; } catch (e: Throwable) { Logger.w(TAG, "Failed to post comment", e); - UIDialogs.toast(context, "Failed to post comment: " + e.message); + UIDialogs.toast(context, context.getString(R.string.failed_to_post_comment) + " ${e.message}"); } }; diff --git a/app/src/main/java/com/futo/platformplayer/views/items/ActiveDownloadItem.kt b/app/src/main/java/com/futo/platformplayer/views/items/ActiveDownloadItem.kt index 5b93bffc..fa393dd9 100644 --- a/app/src/main/java/com/futo/platformplayer/views/items/ActiveDownloadItem.kt +++ b/app/src/main/java/com/futo/platformplayer/views/items/ActiveDownloadItem.kt @@ -110,9 +110,9 @@ class ActiveDownloadItem: LinearLayout { _videoSpeed.text = "${_download.downloadSpeed.toHumanBytesSpeed()} ${(_download.progress * 100).toInt()}%"; _videoState.text = if(!Settings.instance.downloads.shouldDownload()) - "Waiting for unmetered" + (if(!_download.error.isNullOrEmpty()) "\n(Last error: " + _download.error + ")" else ""); + context.getString(R.string.waiting_for_unmetered) + (if(!_download.error.isNullOrEmpty()) "\n(" + context.getString(R.string.last_error) + ": " + _download.error + ")" else ""); else if(_download.state == VideoDownload.State.QUEUED && !_download.error.isNullOrEmpty()) - _download.state.toString() + "\n(Last error: " + _download.error + ")"; + _download.state.toString() + "\n(" + context.getString(R.string.last_error) + ": " + _download.error + ")"; else _download.state.toString(); _videoState.setTextColor(Color.GRAY); @@ -123,7 +123,7 @@ class ActiveDownloadItem: LinearLayout { }; VideoDownload.State.ERROR -> { _videoState.setTextColor(Color.RED); - _videoState.text = _download.error ?: "Error"; + _videoState.text = _download.error ?: context.getString(R.string.error); _videoBar.visibility = GONE; _videoSpeed.visibility = GONE; } diff --git a/app/src/main/java/com/futo/platformplayer/views/overlays/LiveChatOverlay.kt b/app/src/main/java/com/futo/platformplayer/views/overlays/LiveChatOverlay.kt index 8f0e0e09..ae7561bc 100644 --- a/app/src/main/java/com/futo/platformplayer/views/overlays/LiveChatOverlay.kt +++ b/app/src/main/java/com/futo/platformplayer/views/overlays/LiveChatOverlay.kt @@ -204,9 +204,9 @@ class LiveChatOverlay : LinearLayout { _window = window; if(viewerCount != null) - _textViewers.text = viewerCount.toHumanNumber() + " viewers"; + _textViewers.text = viewerCount.toHumanNumber() + " " + context.getString(R.string.viewers); else if(manager != null) - _textViewers.text = manager.viewCount.toHumanNumber() + " viewers"; + _textViewers.text = manager.viewCount.toHumanNumber() + " " + context.getString(R.string.viewers); else _textViewers.text = ""; @@ -239,7 +239,7 @@ class LiveChatOverlay : LinearLayout { } else if(event is LiveEventViewCount) scope.launch(Dispatchers.Main) { - _textViewers.text = "${event.viewCount.toLong().toHumanNumber()} viewers"; + _textViewers.text = "${event.viewCount.toLong().toHumanNumber()} " + context.getString(R.string.viewers); } else handleLiveEvent(event); diff --git a/app/src/main/java/com/futo/platformplayer/views/overlays/QueueEditorOverlay.kt b/app/src/main/java/com/futo/platformplayer/views/overlays/QueueEditorOverlay.kt index 34955187..2d7df204 100644 --- a/app/src/main/java/com/futo/platformplayer/views/overlays/QueueEditorOverlay.kt +++ b/app/src/main/java/com/futo/platformplayer/views/overlays/QueueEditorOverlay.kt @@ -25,13 +25,13 @@ class QueueEditorOverlay : LinearLayout { _editor.onVideoRemoved.subscribe { v -> StatePlayer.instance.removeFromQueue(v) } _editor.onVideoClicked.subscribe { v -> StatePlayer.instance.setQueuePosition(v) } - _topbar.setInfo("Queue", ""); + _topbar.setInfo(context.getString(R.string.queue), ""); } fun updateQueue() { val queue = StatePlayer.instance.getQueue(); _editor.setVideos(queue, true); - _topbar.setInfo("Queue", "${queue.size} videos"); + _topbar.setInfo(context.getString(R.string.queue), "${queue.size} " + context.getString(R.string.videos)); } fun cleanup() { diff --git a/app/src/main/java/com/futo/platformplayer/views/overlays/RepliesOverlay.kt b/app/src/main/java/com/futo/platformplayer/views/overlays/RepliesOverlay.kt index 122d3d27..44cb6020 100644 --- a/app/src/main/java/com/futo/platformplayer/views/overlays/RepliesOverlay.kt +++ b/app/src/main/java/com/futo/platformplayer/views/overlays/RepliesOverlay.kt @@ -38,7 +38,7 @@ class RepliesOverlay : LinearLayout { _commentsList.onCommentsLoaded.subscribe { count -> if (_readonly && count == 0) { - UIDialogs.toast(context, "Expected at least one reply but no replies were returned by the server"); + UIDialogs.toast(context, context.getString(R.string.expected_at_least_one_reply_but_no_replies_were_returned_by_the_server)); } } @@ -46,7 +46,7 @@ class RepliesOverlay : LinearLayout { val replyCount = c.replyCount; var metadata = ""; if (replyCount != null && replyCount > 0) { - metadata += "$replyCount replies"; + metadata += "$replyCount " + context.getString(R.string.replies); } if (c is PolycentricPlatformComment) { @@ -57,7 +57,7 @@ class RepliesOverlay : LinearLayout { }; _topbar.onClose.subscribe(this, onClose::emit); - _topbar.setInfo("Replies", ""); + _topbar.setInfo(context.getString(R.string.Replies), ""); } fun load(readonly: Boolean, metadata: String, contextUrl: String?, ref: Protocol.Reference?, loader: suspend () -> IPager, onCommentAdded: ((comment: IPlatformComment) -> Unit)? = null) { @@ -69,7 +69,7 @@ class RepliesOverlay : LinearLayout { _addCommentView.setContext(contextUrl, ref); } - _topbar.setInfo("Replies", metadata); + _topbar.setInfo(context.getString(R.string.Replies), metadata); _commentsList.load(readonly, loader); _onCommentAdded = onCommentAdded; } diff --git a/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuFilters.kt b/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuFilters.kt index 4bac1c6b..13f382ae 100644 --- a/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuFilters.kt +++ b/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuFilters.kt @@ -3,6 +3,7 @@ package com.futo.platformplayer.views.overlays.slideup import android.view.View import android.view.ViewGroup import androidx.lifecycle.lifecycleScope +import com.futo.platformplayer.R import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.models.FilterGroup @@ -34,7 +35,7 @@ class SlideUpMenuFilters { _container = container; _enabledClientsIds = enabledClientsIds; _filterValues = filterValues; - _slideUpMenuOverlay = SlideUpMenuOverlay(_container.context, _container, "Filters", "Done", true, listOf()); + _slideUpMenuOverlay = SlideUpMenuOverlay(_container.context, _container, container.context.getString(R.string.filters), container.context.getString(R.string.done), true, listOf()); _slideUpMenuOverlay.onOK.subscribe { onOK.emit(_enabledClientsIds, _changed); _slideUpMenuOverlay.hide(); @@ -84,7 +85,7 @@ class SlideUpMenuFilters { val caps = commonCapabilities; val items = arrayListOf(); - val group = SlideUpMenuRadioGroup(_container.context, "Sources", StatePlatform.instance.getSortedEnabledClient().map { Pair(it.name, it.id) }, + val group = SlideUpMenuRadioGroup(_container.context, _container.context.getString(R.string.sources), StatePlatform.instance.getSortedEnabledClient().map { Pair(it.name, it.id) }, _enabledClientsIds, true, true); group.onSelectedChange.subscribe { diff --git a/app/src/main/java/com/futo/platformplayer/views/pills/PillRatingLikesDislikes.kt b/app/src/main/java/com/futo/platformplayer/views/pills/PillRatingLikesDislikes.kt index e4fa2d40..f56feced 100644 --- a/app/src/main/java/com/futo/platformplayer/views/pills/PillRatingLikesDislikes.kt +++ b/app/src/main/java/com/futo/platformplayer/views/pills/PillRatingLikesDislikes.kt @@ -48,10 +48,10 @@ class PillRatingLikesDislikes : LinearLayout { _iconDislikes = findViewById(R.id.pill_dislike_icon); _iconLikes = findViewById(R.id.pill_like_icon); - _iconLikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, "Please login to like") { like(it) }; }; - _textLikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, "Please login to like") { like(it) }; }; - _iconDislikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, "Please login to dislike") { dislike(it) }; }; - _textDislikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, "Please login to dislike") { dislike(it) }; }; + _iconLikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, context.getString(R.string.please_login_to_like)) { like(it) }; }; + _textLikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, context.getString(R.string.please_login_to_like)) { like(it) }; }; + _iconDislikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, context.getString(R.string.please_login_to_dislike)) { dislike(it) }; }; + _textDislikes.setOnClickListener { StatePolycentric.instance.requireLogin(context, context.getString(R.string.please_login_to_dislike)) { dislike(it) }; }; } fun setRating(rating: IRating, hasLiked: Boolean = false, hasDisliked: Boolean = false) { diff --git a/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt b/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt index a729bd13..9841bfd9 100644 --- a/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt +++ b/app/src/main/java/com/futo/platformplayer/views/segments/CommentsList.kt @@ -34,7 +34,7 @@ class CommentsList : ConstraintLayout { } .exception { Logger.e(TAG, "Failed to load comments.", it); - UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load comments. " + (it.message ?: ""), it, ::fetchComments); + UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_comments) + (it.message ?: ""), it, ::fetchComments); setLoading(false); } else TaskHandler(IPlatformVideoDetails::class.java, StateApp.instance.scopeGetter); diff --git a/app/src/main/java/com/futo/platformplayer/views/sources/SourceHeaderView.kt b/app/src/main/java/com/futo/platformplayer/views/sources/SourceHeaderView.kt index b3f63c76..fd8679e3 100644 --- a/app/src/main/java/com/futo/platformplayer/views/sources/SourceHeaderView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/sources/SourceHeaderView.kt @@ -82,17 +82,17 @@ class SourceHeaderView : LinearLayout { if (!config.scriptPublicKey.isNullOrEmpty() && !config.scriptSignature.isNullOrEmpty()) { if (script == null) { _sourceSignature.setTextColor(Color.rgb(0xAC, 0xAC, 0xAC)); - _sourceSignature.text = "Script is not available"; + _sourceSignature.text = context.getString(R.string.script_is_not_available); } else if (config.validate(script)) { _sourceSignature.setTextColor(Color.rgb(0, 255, 0)); - _sourceSignature.text = "Signature is valid"; + _sourceSignature.text = context.getString(R.string.signature_is_valid); } else { _sourceSignature.setTextColor(Color.rgb(255, 0, 0)); - _sourceSignature.text = "Signature is invalid"; + _sourceSignature.text = context.getString(R.string.signature_is_invalid); } } else { _sourceSignature.setTextColor(Color.rgb(255, 0, 0)); - _sourceSignature.text = "No signature available"; + _sourceSignature.text = context.getString(R.string.no_signature_available); } } diff --git a/app/src/main/java/com/futo/platformplayer/views/subscriptions/SubscribeButton.kt b/app/src/main/java/com/futo/platformplayer/views/subscriptions/SubscribeButton.kt index c0c7f6e4..dba1707d 100644 --- a/app/src/main/java/com/futo/platformplayer/views/subscriptions/SubscribeButton.kt +++ b/app/src/main/java/com/futo/platformplayer/views/subscriptions/SubscribeButton.kt @@ -70,14 +70,14 @@ class SubscribeButton : LinearLayout { private fun handleSubscribe(channel: IPlatformChannel) { setIsLoading(false); StateSubscriptions.instance.addSubscription(channel); - UIDialogs.toast(context, "Subscribed to ${channel.name}"); + UIDialogs.toast(context, context.getString(R.string.subscribed_to) + channel.name); setIsSubscribed(true); } private fun handleUnSubscribe(url: String) { setIsLoading(false); val removed = StateSubscriptions.instance.removeSubscription(url); if (removed != null) - UIDialogs.toast(context, "Unsubscribed from ${removed!!.channel.name}"); + UIDialogs.toast(context, context.getString(R.string.unsubscribed_from) + removed.channel.name); setIsSubscribed(false); } diff --git a/app/src/main/java/com/futo/platformplayer/views/videometa/UpNextView.kt b/app/src/main/java/com/futo/platformplayer/views/videometa/UpNextView.kt index f217f145..8d9f136a 100644 --- a/app/src/main/java/com/futo/platformplayer/views/videometa/UpNextView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/videometa/UpNextView.kt @@ -149,7 +149,7 @@ class UpNextView : LinearLayout { val metadataTokens = mutableListOf(); if (nextItem.viewCount > 0) { - metadataTokens.add("${nextItem.viewCount.toHumanNumber()} views"); + metadataTokens.add("${nextItem.viewCount.toHumanNumber()} " + context.getString(R.string.views)); } if (nextItem.datetime != null) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3a145ae..391e80bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -350,7 +350,6 @@ Pay Standard Payment Methods Stripe is a secure online payment service that accepts major credit cards, debit cards, and various localized payment methods. - LIVE Add a comment… Dismiss Scan a QR code to install @@ -527,13 +526,13 @@ Scan a QR Code Not implemented yet.. Unknown Context - Something went wrong... missing stack trace? + Something went wrong… missing stack trace? Logs already submitted. No logs found. Failed automated share, share manually? Shared {id} Unhandled exception in VS - Send exception to developers... + Send exception to developers… Your license key has been set!\nAn app restart might be required. Invalid license format Unknown content format @@ -610,6 +609,7 @@ Expected media content, found Failed to load post. replies + Replies Plugin settings saved Plugin settings These settings are defined by the plugin @@ -684,6 +684,52 @@ Failed to retry for live stream This app is in development. Please submit bug reports and understand that many features are incomplete. Please use at least 3 characters + Are you sure you want to delete this video? + Tap to open + watching + available in + seconds + Please login to post a comment + Failed to post comment: + Waiting for unmetered + Last error + Error + Filters + viewers + Expected at least one reply but no replies were returned by the server + Please login to like + Please login to dislike + "Failed to load comments. " + Script is not available + Signature is valid + Signature is invalid + No signature available + "Subscribed to " + "Unsubscribed from " + You don\'t have any automatic backups + An old backup is available + Would you like to restore this backup? + Override + Data Retry + No downloads available + No downloadable sources (yet) + None + Audio Only + Download Video + Fetching video details + Failed to fetch details for download + Target Resolution + Target Bitrate + Low Bitrate + High Bitrate + Actions + Download the video + Video Options + Change Pins + Decide which buttons should be pinned + Select your pins in order + More Options + Save FastCast ChromeCast