mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 05:23:41 +00:00
Import export settings
This commit is contained in:
parent
9826ee4d36
commit
2be6f04e37
@ -64,6 +64,10 @@ final class AccountsModel: ObservableObject {
|
||||
)
|
||||
}
|
||||
|
||||
func find(_ id: Account.ID) -> Account? {
|
||||
all.first { $0.id == id }
|
||||
}
|
||||
|
||||
func configureAccount() {
|
||||
if let account = lastUsed ??
|
||||
InstancesModel.shared.lastUsed?.anonymousAccount ??
|
||||
@ -108,8 +112,8 @@ final class AccountsModel: ObservableObject {
|
||||
Defaults[.accounts].first { $0.id == id }
|
||||
}
|
||||
|
||||
static func add(instance: Instance, name: String, username: String, password: String) -> Account {
|
||||
let account = Account(instanceID: instance.id, name: name, urlString: instance.apiURLString)
|
||||
static func add(instance: Instance, id: String? = UUID().uuidString, name: String, username: String, password: String) -> Account {
|
||||
let account = Account(id: id, instanceID: instance.id, name: name, urlString: instance.apiURLString)
|
||||
Defaults[.accounts].append(account)
|
||||
|
||||
setCredentials(account, username: username, password: password)
|
||||
|
@ -68,4 +68,8 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(apiURL)
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
AccountsModel.shared.all.filter { $0.instanceID == id }
|
||||
}
|
||||
}
|
||||
|
@ -42,15 +42,23 @@ final class InstancesModel: ObservableObject {
|
||||
Defaults[.accounts].filter { $0.instanceID == id }
|
||||
}
|
||||
|
||||
func add(app: VideosApp, name: String, url: String) -> Instance {
|
||||
func add(id: String? = UUID().uuidString, app: VideosApp, name: String, url: String) -> Instance {
|
||||
let instance = Instance(
|
||||
app: app, id: UUID().uuidString, name: name, apiURLString: standardizedURL(url)
|
||||
app: app, id: id, name: name, apiURLString: standardizedURL(url)
|
||||
)
|
||||
Defaults[.instances].append(instance)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func insert(id: String? = UUID().uuidString, app: VideosApp, name: String, url: String) -> Instance {
|
||||
if let instance = Defaults[.instances].first(where: { $0.apiURL.absoluteString == standardizedURL(url) }) {
|
||||
return instance
|
||||
}
|
||||
|
||||
return add(id: id, app: app, name: name, url: url)
|
||||
}
|
||||
|
||||
func setFrontendURL(_ instance: Instance, _ url: String) {
|
||||
if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
|
||||
var instance = Defaults[.instances][index]
|
||||
|
@ -25,6 +25,7 @@ struct FavoritesModel {
|
||||
}
|
||||
|
||||
func add(_ item: FavoriteItem) {
|
||||
if contains(item) { return }
|
||||
all.append(item)
|
||||
}
|
||||
|
||||
@ -122,4 +123,12 @@ struct FavoritesModel {
|
||||
func widgetSettings(_ item: FavoriteItem) -> WidgetSettings {
|
||||
widgetsSettings.first { $0.id == item.widgetSettingsKey } ?? WidgetSettings(id: item.widgetSettingsKey)
|
||||
}
|
||||
|
||||
func updateWidgetSettings(_ settings: WidgetSettings) {
|
||||
if let index = widgetsSettings.firstIndex(where: { $0.id == settings.id }) {
|
||||
widgetsSettings[index] = settings
|
||||
} else {
|
||||
widgetsSettings.append(settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class AdvancedSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"showPlayNowInBackendContextMenu": Defaults[.showPlayNowInBackendContextMenu],
|
||||
"showMPVPlaybackStats": Defaults[.showMPVPlaybackStats],
|
||||
"mpvEnableLogging": Defaults[.mpvEnableLogging],
|
||||
"mpvCacheSecs": Defaults[.mpvCacheSecs],
|
||||
"mpvCachePauseWait": Defaults[.mpvCachePauseWait],
|
||||
"showCacheStatus": Defaults[.showCacheStatus],
|
||||
"feedCacheSize": Defaults[.feedCacheSize]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class BrowsingSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"showHome": Defaults[.showHome],
|
||||
"showOpenActionsInHome": Defaults[.showOpenActionsInHome],
|
||||
"showQueueInHome": Defaults[.showQueueInHome],
|
||||
"showFavoritesInHome": Defaults[.showFavoritesInHome],
|
||||
"favorites": Defaults[.favorites].compactMap { jsonFromString(FavoriteItem.bridge.serialize($0)) },
|
||||
"widgetsSettings": Defaults[.widgetsSettings].compactMap { widgetSettingsJSON($0) },
|
||||
"startupSection": Defaults[.startupSection].rawValue,
|
||||
"visibleSections": Defaults[.visibleSections].compactMap { $0.rawValue },
|
||||
"showOpenActionsToolbarItem": Defaults[.showOpenActionsToolbarItem],
|
||||
"accountPickerDisplaysAnonymousAccounts": Defaults[.accountPickerDisplaysAnonymousAccounts],
|
||||
"showUnwatchedFeedBadges": Defaults[.showUnwatchedFeedBadges],
|
||||
"expandChannelDescription": Defaults[.expandChannelDescription],
|
||||
"keepChannelsWithUnwatchedFeedOnTop": Defaults[.keepChannelsWithUnwatchedFeedOnTop],
|
||||
"showChannelAvatarInChannelsLists": Defaults[.showChannelAvatarInChannelsLists],
|
||||
"showChannelAvatarInVideosListing": Defaults[.showChannelAvatarInVideosListing],
|
||||
"playerButtonSingleTapGesture": Defaults[.playerButtonSingleTapGesture].rawValue,
|
||||
"playerButtonDoubleTapGesture": Defaults[.playerButtonDoubleTapGesture].rawValue,
|
||||
"playerButtonShowsControlButtonsWhenMinimized": Defaults[.playerButtonShowsControlButtonsWhenMinimized],
|
||||
"playerButtonIsExpanded": Defaults[.playerButtonIsExpanded],
|
||||
"playerBarMaxWidth": Defaults[.playerBarMaxWidth],
|
||||
"channelOnThumbnail": Defaults[.channelOnThumbnail],
|
||||
"timeOnThumbnail": Defaults[.timeOnThumbnail],
|
||||
"roundedThumbnails": Defaults[.roundedThumbnails],
|
||||
"thumbnailsQuality": Defaults[.thumbnailsQuality].rawValue
|
||||
]
|
||||
}
|
||||
|
||||
override var platformJSON: JSON {
|
||||
var export = JSON()
|
||||
|
||||
#if os(iOS)
|
||||
export["showDocuments"].bool = Defaults[.showDocuments]
|
||||
export["lockPortraitWhenBrowsing"].bool = Defaults[.lockPortraitWhenBrowsing]
|
||||
#endif
|
||||
|
||||
#if !os(tvOS)
|
||||
export["accountPickerDisplaysUsername"].bool = Defaults[.accountPickerDisplaysUsername]
|
||||
#endif
|
||||
|
||||
return export
|
||||
}
|
||||
|
||||
private func widgetSettingsJSON(_ settings: WidgetSettings) -> JSON {
|
||||
var json = JSON()
|
||||
json.dictionaryObject = WidgetSettingsBridge().serialize(settings)
|
||||
return json
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class ConstrolsSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"avPlayerUsesSystemControls": Defaults[.avPlayerUsesSystemControls],
|
||||
"horizontalPlayerGestureEnabled": Defaults[.horizontalPlayerGestureEnabled],
|
||||
"seekGestureSensitivity": Defaults[.seekGestureSensitivity],
|
||||
"seekGestureSpeed": Defaults[.seekGestureSpeed],
|
||||
"playerControlsLayout": Defaults[.playerControlsLayout].rawValue,
|
||||
"fullScreenPlayerControlsLayout": Defaults[.fullScreenPlayerControlsLayout].rawValue,
|
||||
"systemControlsCommands": Defaults[.systemControlsCommands].rawValue,
|
||||
"buttonBackwardSeekDuration": Defaults[.buttonBackwardSeekDuration],
|
||||
"buttonForwardSeekDuration": Defaults[.buttonForwardSeekDuration],
|
||||
"gestureBackwardSeekDuration": Defaults[.gestureBackwardSeekDuration],
|
||||
"gestureForwardSeekDuration": Defaults[.gestureForwardSeekDuration],
|
||||
"systemControlsSeekDuration": Defaults[.systemControlsSeekDuration],
|
||||
"playerControlsSettingsEnabled": Defaults[.playerControlsSettingsEnabled],
|
||||
"playerControlsCloseEnabled": Defaults[.playerControlsCloseEnabled],
|
||||
"playerControlsRestartEnabled": Defaults[.playerControlsRestartEnabled],
|
||||
"playerControlsAdvanceToNextEnabled": Defaults[.playerControlsAdvanceToNextEnabled],
|
||||
"playerControlsPlaybackModeEnabled": Defaults[.playerControlsPlaybackModeEnabled],
|
||||
"playerControlsMusicModeEnabled": Defaults[.playerControlsMusicModeEnabled],
|
||||
"playerActionsButtonLabelStyle": Defaults[.playerActionsButtonLabelStyle].rawValue,
|
||||
"actionButtonShareEnabled": Defaults[.actionButtonShareEnabled],
|
||||
"actionButtonAddToPlaylistEnabled": Defaults[.actionButtonAddToPlaylistEnabled],
|
||||
"actionButtonSubscribeEnabled": Defaults[.actionButtonSubscribeEnabled],
|
||||
"actionButtonSettingsEnabled": Defaults[.actionButtonSettingsEnabled],
|
||||
"actionButtonHideEnabled": Defaults[.actionButtonHideEnabled],
|
||||
"actionButtonCloseEnabled": Defaults[.actionButtonCloseEnabled],
|
||||
"actionButtonFullScreenEnabled": Defaults[.actionButtonFullScreenEnabled],
|
||||
"actionButtonPipEnabled": Defaults[.actionButtonPipEnabled],
|
||||
"actionButtonLockOrientationEnabled": Defaults[.actionButtonLockOrientationEnabled],
|
||||
"actionButtonRestartEnabled": Defaults[.actionButtonRestartEnabled],
|
||||
"actionButtonAdvanceToNextItemEnabled": Defaults[.actionButtonAdvanceToNextItemEnabled],
|
||||
"actionButtonMusicModeEnabled": Defaults[.actionButtonMusicModeEnabled]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class HistorySettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"saveRecents": Defaults[.saveRecents],
|
||||
"saveHistory": Defaults[.saveHistory],
|
||||
"showWatchingProgress": Defaults[.showWatchingProgress],
|
||||
"saveLastPlayed": Defaults[.saveLastPlayed],
|
||||
|
||||
"watchedVideoPlayNowBehavior": Defaults[.watchedVideoPlayNowBehavior].rawValue,
|
||||
"watchedThreshold": Defaults[.watchedThreshold],
|
||||
"resetWatchedStatusOnPlaying": Defaults[.resetWatchedStatusOnPlaying],
|
||||
|
||||
"watchedVideoStyle": Defaults[.watchedVideoStyle].rawValue,
|
||||
"watchedVideoBadgeColor": Defaults[.watchedVideoBadgeColor].rawValue,
|
||||
"showToggleWatchedStatusButton": Defaults[.showToggleWatchedStatusButton]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class LocationsSettingsGroupExporter: SettingsGroupExporter {
|
||||
var includePublicInstances = true
|
||||
var includeInstances = true
|
||||
var includeAccounts = true
|
||||
var includeAccountsUnencryptedPasswords = false
|
||||
|
||||
init(includePublicInstances: Bool = true, includeInstances: Bool = true, includeAccounts: Bool = true, includeAccountsUnencryptedPasswords: Bool = false) {
|
||||
self.includePublicInstances = includePublicInstances
|
||||
self.includeInstances = includeInstances
|
||||
self.includeAccounts = includeAccounts
|
||||
self.includeAccountsUnencryptedPasswords = includeAccountsUnencryptedPasswords
|
||||
}
|
||||
|
||||
override var globalJSON: JSON {
|
||||
var json = JSON()
|
||||
|
||||
if includePublicInstances {
|
||||
json["instancesManifest"].string = Defaults[.instancesManifest]
|
||||
json["countryOfPublicInstances"].string = Defaults[.countryOfPublicInstances] ?? ""
|
||||
}
|
||||
|
||||
if includeInstances {
|
||||
json["instances"].arrayObject = Defaults[.instances].compactMap { instanceJSON($0) }
|
||||
}
|
||||
|
||||
if includeAccounts {
|
||||
json["accounts"].arrayObject = Defaults[.accounts].compactMap { account in
|
||||
var account = account
|
||||
let (username, password) = AccountsModel.getCredentials(account)
|
||||
account.username = username ?? ""
|
||||
if includeAccountsUnencryptedPasswords {
|
||||
account.password = password ?? ""
|
||||
}
|
||||
|
||||
return accountJSON(account).dictionaryObject
|
||||
}
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
private func instanceJSON(_ instance: Instance) -> JSON {
|
||||
var json = JSON()
|
||||
json.dictionaryObject = InstancesBridge().serialize(instance)
|
||||
return json
|
||||
}
|
||||
|
||||
private func accountJSON(_ account: Account) -> JSON {
|
||||
var json = JSON()
|
||||
json.dictionaryObject = AccountsBridge().serialize(account)
|
||||
return json
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class OtherDataSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"lastAccountID": Defaults[.lastAccountID] ?? "",
|
||||
"lastInstanceID": Defaults[.lastInstanceID] ?? "",
|
||||
|
||||
"playerRate": Defaults[.playerRate],
|
||||
|
||||
"trendingCategory": Defaults[.trendingCategory].rawValue,
|
||||
"trendingCountry": Defaults[.trendingCountry].rawValue,
|
||||
|
||||
"subscriptionsViewPage": Defaults[.subscriptionsViewPage].rawValue,
|
||||
"subscriptionsListingStyle": Defaults[.subscriptionsListingStyle].rawValue,
|
||||
"popularListingStyle": Defaults[.popularListingStyle].rawValue,
|
||||
"trendingListingStyle": Defaults[.trendingListingStyle].rawValue,
|
||||
"playlistListingStyle": Defaults[.playlistListingStyle].rawValue,
|
||||
"channelPlaylistListingStyle": Defaults[.channelPlaylistListingStyle].rawValue,
|
||||
"searchListingStyle": Defaults[.searchListingStyle].rawValue,
|
||||
|
||||
"hideShorts": Defaults[.hideShorts],
|
||||
"hideWatched": Defaults[.hideWatched]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class PlayerSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"playerInstanceID": Defaults[.playerInstanceID] ?? "",
|
||||
"pauseOnHidingPlayer": Defaults[.pauseOnHidingPlayer],
|
||||
"closeVideoOnEOF": Defaults[.closeVideoOnEOF],
|
||||
"expandVideoDescription": Defaults[.expandVideoDescription],
|
||||
"collapsedLinesDescription": Defaults[.collapsedLinesDescription],
|
||||
"showChapters": Defaults[.showChapters],
|
||||
"expandChapters": Defaults[.expandChapters],
|
||||
"showRelated": Defaults[.showRelated],
|
||||
"showInspector": Defaults[.showInspector].rawValue,
|
||||
"playerSidebar": Defaults[.playerSidebar].rawValue,
|
||||
"showKeywords": Defaults[.showKeywords],
|
||||
"enableReturnYouTubeDislike": Defaults[.enableReturnYouTubeDislike],
|
||||
"closePiPOnNavigation": Defaults[.closePiPOnNavigation],
|
||||
"closePiPOnOpeningPlayer": Defaults[.closePiPOnOpeningPlayer],
|
||||
"closePlayerOnOpeningPiP": Defaults[.closePlayerOnOpeningPiP]
|
||||
]
|
||||
}
|
||||
|
||||
override var platformJSON: JSON {
|
||||
var export = JSON()
|
||||
|
||||
#if !os(macOS)
|
||||
export["pauseOnEnteringBackground"].bool = Defaults[.pauseOnEnteringBackground]
|
||||
#endif
|
||||
|
||||
#if !os(tvOS)
|
||||
export["showScrollToTopInComments"].bool = Defaults[.showScrollToTopInComments]
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
export["honorSystemOrientationLock"].bool = Defaults[.honorSystemOrientationLock]
|
||||
export["enterFullscreenInLandscape"].bool = Defaults[.enterFullscreenInLandscape]
|
||||
export["rotateToLandscapeOnEnterFullScreen"].string = Defaults[.rotateToLandscapeOnEnterFullScreen].rawValue
|
||||
#endif
|
||||
|
||||
return export
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class QualitySettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"batteryCellularProfile": Defaults[.batteryCellularProfile],
|
||||
"batteryNonCellularProfile": Defaults[.batteryNonCellularProfile],
|
||||
"chargingCellularProfile": Defaults[.chargingCellularProfile],
|
||||
"chargingNonCellularProfile": Defaults[.chargingNonCellularProfile],
|
||||
"forceAVPlayerForLiveStreams": Defaults[.forceAVPlayerForLiveStreams],
|
||||
"qualityProfiles": Defaults[.qualityProfiles].compactMap { qualityProfileJSON($0) }
|
||||
]
|
||||
}
|
||||
|
||||
func qualityProfileJSON(_ profile: QualityProfile) -> JSON {
|
||||
var json = JSON()
|
||||
json.dictionaryObject = QualityProfileBridge().serialize(profile)
|
||||
return json
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class RecentlyOpenedExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"recentlyOpened": Defaults[.recentlyOpened].compactMap { recentItemJSON($0) }
|
||||
]
|
||||
}
|
||||
|
||||
private func recentItemJSON(_ recentItem: RecentItem) -> JSON {
|
||||
var json = JSON()
|
||||
json.dictionaryObject = RecentItemBridge().serialize(recentItem)
|
||||
return json
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
class SettingsGroupExporter { // swiftlint:disable:this final_class
|
||||
var globalJSON: JSON {
|
||||
[]
|
||||
}
|
||||
|
||||
var platformJSON: JSON {
|
||||
[]
|
||||
}
|
||||
|
||||
var exportJSON: JSON {
|
||||
var json = globalJSON
|
||||
|
||||
if !platformJSON.isEmpty {
|
||||
try? json.merge(with: platformJSON)
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
func jsonFromString(_ string: String?) -> JSON? {
|
||||
if let data = string?.data(using: .utf8, allowLossyConversion: false),
|
||||
let json = try? JSON(data: data)
|
||||
{
|
||||
return json
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
final class SponsorBlockSettingsGroupExporter: SettingsGroupExporter {
|
||||
override var globalJSON: JSON {
|
||||
[
|
||||
"sponsorBlockInstance": Defaults[.sponsorBlockInstance],
|
||||
"sponsorBlockCategories": Array(Defaults[.sponsorBlockCategories])
|
||||
]
|
||||
}
|
||||
}
|
193
Model/Import Export Settings/ImportExportSettingsModel.swift
Normal file
193
Model/Import Export Settings/ImportExportSettingsModel.swift
Normal file
@ -0,0 +1,193 @@
|
||||
import Defaults
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SwiftyJSON
|
||||
|
||||
final class ImportExportSettingsModel: ObservableObject {
|
||||
static let shared = ImportExportSettingsModel()
|
||||
|
||||
static var exportFile: URL {
|
||||
YatteeApp.settingsExportDirectory
|
||||
.appendingPathComponent("Yattee Settings from \(Constants.deviceName).\(settingsExtension)")
|
||||
}
|
||||
|
||||
static var settingsExtension: String {
|
||||
"yatteesettings"
|
||||
}
|
||||
|
||||
enum ExportGroup: String, Identifiable, CaseIterable {
|
||||
case browsingSettings
|
||||
case playerSettings
|
||||
case controlsSettings
|
||||
case qualitySettings
|
||||
case historySettings
|
||||
case sponsorBlockSettings
|
||||
case advancedSettings
|
||||
|
||||
case locationsSettings
|
||||
case instances
|
||||
case accounts
|
||||
case accountsUnencryptedPasswords
|
||||
|
||||
case recentlyOpened
|
||||
case otherData
|
||||
|
||||
static var settingsGroups: [Self] {
|
||||
[.browsingSettings, .playerSettings, .controlsSettings, .qualitySettings, .historySettings, .sponsorBlockSettings, .advancedSettings]
|
||||
}
|
||||
|
||||
static var locationsGroups: [Self] {
|
||||
[.locationsSettings, .instances, .accounts, .accountsUnencryptedPasswords]
|
||||
}
|
||||
|
||||
static var otherGroups: [Self] {
|
||||
[.recentlyOpened, .otherData]
|
||||
}
|
||||
|
||||
var id: RawValue {
|
||||
rawValue
|
||||
}
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .browsingSettings:
|
||||
"Browsing"
|
||||
case .playerSettings:
|
||||
"Player"
|
||||
case .controlsSettings:
|
||||
"Controls"
|
||||
case .qualitySettings:
|
||||
"Quality"
|
||||
case .historySettings:
|
||||
"History"
|
||||
case .sponsorBlockSettings:
|
||||
"SponsorBlock"
|
||||
case .locationsSettings:
|
||||
"Public Locations"
|
||||
case .instances:
|
||||
"Custom Locations"
|
||||
case .accounts:
|
||||
"Accounts"
|
||||
case .accountsUnencryptedPasswords:
|
||||
"Accounts passwords (unencrypted)"
|
||||
case .advancedSettings:
|
||||
"Advanced"
|
||||
case .recentlyOpened:
|
||||
"Recents"
|
||||
case .otherData:
|
||||
"Other data"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var selectedExportGroups = Set<ExportGroup>()
|
||||
static var defaultExportGroups = Set<ExportGroup>([
|
||||
.browsingSettings,
|
||||
.playerSettings,
|
||||
.controlsSettings,
|
||||
.qualitySettings,
|
||||
.historySettings,
|
||||
.sponsorBlockSettings,
|
||||
.locationsSettings,
|
||||
.instances,
|
||||
.accounts,
|
||||
.advancedSettings
|
||||
])
|
||||
|
||||
@Published var isExportInProgress = false
|
||||
|
||||
private var navigation = NavigationModel.shared
|
||||
private var settings = SettingsModel.shared
|
||||
|
||||
func toggleExportGroupSelection(_ group: ExportGroup) {
|
||||
if isGroupSelected(group) {
|
||||
selectedExportGroups.remove(group)
|
||||
} else {
|
||||
selectedExportGroups.insert(group)
|
||||
}
|
||||
|
||||
removeNotEnabledSelectedGroups()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
isExportInProgress = false
|
||||
selectedExportGroups = Self.defaultExportGroups
|
||||
}
|
||||
|
||||
func reset(_ model: ImportSettingsFileModel? = nil) {
|
||||
reset()
|
||||
|
||||
guard let model else { return }
|
||||
|
||||
selectedExportGroups = selectedExportGroups.filter { model.isGroupIncludedInFile($0) }
|
||||
}
|
||||
|
||||
func exportAction() {
|
||||
DispatchQueue.global(qos: .background).async { [weak self] in
|
||||
var writingOptions: JSONSerialization.WritingOptions = []
|
||||
#if DEBUG
|
||||
writingOptions.insert(.prettyPrinted)
|
||||
writingOptions.insert(.sortedKeys)
|
||||
#endif
|
||||
try? self?.jsonForExport?.rawString(options: writingOptions)?.write(to: Self.exportFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
#if os(macOS)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.isExportInProgress = false
|
||||
}
|
||||
NSWorkspace.shared.selectFile(Self.exportFile.path, inFileViewerRootedAtPath: YatteeApp.settingsExportDirectory.path)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private var jsonForExport: JSON? {
|
||||
[
|
||||
"metadata": metadataJSON,
|
||||
"browsingSettings": selectedExportGroups.contains(.browsingSettings) ? BrowsingSettingsGroupExporter().exportJSON : JSON(),
|
||||
"playerSettings": selectedExportGroups.contains(.playerSettings) ? PlayerSettingsGroupExporter().exportJSON : JSON(),
|
||||
"controlsSettings": selectedExportGroups.contains(.controlsSettings) ? ConstrolsSettingsGroupExporter().exportJSON : JSON(),
|
||||
"qualitySettings": selectedExportGroups.contains(.qualitySettings) ? QualitySettingsGroupExporter().exportJSON : JSON(),
|
||||
"historySettings": selectedExportGroups.contains(.historySettings) ? HistorySettingsGroupExporter().exportJSON : JSON(),
|
||||
"sponsorBlockSettings": selectedExportGroups.contains(.sponsorBlockSettings) ? SponsorBlockSettingsGroupExporter().exportJSON : JSON(),
|
||||
"locationsSettings": LocationsSettingsGroupExporter(
|
||||
includePublicInstances: isGroupSelected(.locationsSettings),
|
||||
includeInstances: isGroupSelected(.instances),
|
||||
includeAccounts: isGroupSelected(.accounts),
|
||||
includeAccountsUnencryptedPasswords: isGroupSelected(.accountsUnencryptedPasswords)
|
||||
).exportJSON,
|
||||
"advancedSettings": selectedExportGroups.contains(.advancedSettings) ? AdvancedSettingsGroupExporter().exportJSON : JSON(),
|
||||
"recentlyOpened": selectedExportGroups.contains(.recentlyOpened) ? RecentlyOpenedExporter().exportJSON : JSON(),
|
||||
"otherData": selectedExportGroups.contains(.otherData) ? OtherDataSettingsGroupExporter().exportJSON : JSON()
|
||||
]
|
||||
}
|
||||
|
||||
private var metadataJSON: JSON {
|
||||
[
|
||||
"build": YatteeApp.build,
|
||||
"timestamp": "\(Date().timeIntervalSince1970)",
|
||||
"platform": Constants.platform
|
||||
]
|
||||
}
|
||||
|
||||
func isGroupSelected(_ group: ExportGroup) -> Bool {
|
||||
selectedExportGroups.contains(group)
|
||||
}
|
||||
|
||||
func isGroupEnabled(_ group: ExportGroup) -> Bool {
|
||||
switch group {
|
||||
case .accounts:
|
||||
return selectedExportGroups.contains(.instances)
|
||||
case .accountsUnencryptedPasswords:
|
||||
return selectedExportGroups.contains(.instances) && selectedExportGroups.contains(.accounts)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func removeNotEnabledSelectedGroups() {
|
||||
selectedExportGroups = selectedExportGroups.filter { isGroupEnabled($0) }
|
||||
}
|
||||
|
||||
var isExportAvailable: Bool {
|
||||
!selectedExportGroups.isEmpty && !isExportInProgress
|
||||
}
|
||||
}
|
135
Model/Import Export Settings/ImportSettingsFileModel.swift
Normal file
135
Model/Import Export Settings/ImportSettingsFileModel.swift
Normal file
@ -0,0 +1,135 @@
|
||||
import Defaults
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
|
||||
struct ImportSettingsFileModel {
|
||||
let url: URL
|
||||
|
||||
var filename: String {
|
||||
String(url.lastPathComponent.dropLast(ImportExportSettingsModel.settingsExtension.count + 1))
|
||||
}
|
||||
|
||||
var locationsSettingsGroupImporter: LocationsSettingsGroupImporter? {
|
||||
if let locationsSettings = json?.dictionaryValue["locationsSettings"] {
|
||||
return LocationsSettingsGroupImporter(
|
||||
json: locationsSettings,
|
||||
includePublicLocations: importExportModel.isGroupEnabled(.locationsSettings),
|
||||
includedInstancesIDs: sheetViewModel.selectedInstances,
|
||||
includedAccountsIDs: sheetViewModel.selectedAccounts,
|
||||
includedAccountsPasswords: sheetViewModel.importableAccountsPasswords
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var importExportModel = ImportExportSettingsModel.shared
|
||||
var sheetViewModel = ImportSettingsSheetViewModel.shared
|
||||
|
||||
func isGroupIncludedInFile(_ group: ImportExportSettingsModel.ExportGroup) -> Bool {
|
||||
switch group {
|
||||
case .locationsSettings:
|
||||
return isPublicInstancesSettingsGroupInFile || instancesOrAccountsInFile
|
||||
default:
|
||||
return !groupJSON(group).isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
var isPublicInstancesSettingsGroupInFile: Bool {
|
||||
guard let dict = groupJSON(.locationsSettings).dictionary else { return false }
|
||||
|
||||
return dict.keys.contains("instancesManifest") || dict.keys.contains("countryOfPublicInstances")
|
||||
}
|
||||
|
||||
var instancesOrAccountsInFile: Bool {
|
||||
guard let dict = groupJSON(.locationsSettings).dictionary else { return false }
|
||||
|
||||
return (dict.keys.contains("instances") && !(dict["instances"]?.arrayValue.isEmpty ?? true)) ||
|
||||
(dict.keys.contains("accounts") && !(dict["accounts"]?.arrayValue.isEmpty ?? true))
|
||||
}
|
||||
|
||||
func groupJSON(_ group: ImportExportSettingsModel.ExportGroup) -> JSON {
|
||||
json?.dictionaryValue[group.rawValue] ?? .init()
|
||||
}
|
||||
|
||||
func performImport() {
|
||||
if importExportModel.isGroupSelected(.browsingSettings), isGroupIncludedInFile(.browsingSettings) {
|
||||
BrowsingSettingsGroupImporter(json: groupJSON(.browsingSettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.playerSettings), isGroupIncludedInFile(.playerSettings) {
|
||||
PlayerSettingsGroupImporter(json: groupJSON(.playerSettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.controlsSettings), isGroupIncludedInFile(.controlsSettings) {
|
||||
ConstrolsSettingsGroupImporter(json: groupJSON(.controlsSettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.qualitySettings), isGroupIncludedInFile(.qualitySettings) {
|
||||
QualitySettingsGroupImporter(json: groupJSON(.qualitySettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.historySettings), isGroupIncludedInFile(.historySettings) {
|
||||
HistorySettingsGroupImporter(json: groupJSON(.historySettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.sponsorBlockSettings), isGroupIncludedInFile(.sponsorBlockSettings) {
|
||||
SponsorBlockSettingsGroupImporter(json: groupJSON(.sponsorBlockSettings)).performImport()
|
||||
}
|
||||
|
||||
locationsSettingsGroupImporter?.performImport()
|
||||
|
||||
if importExportModel.isGroupSelected(.advancedSettings), isGroupIncludedInFile(.advancedSettings) {
|
||||
AdvancedSettingsGroupImporter(json: groupJSON(.advancedSettings)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.recentlyOpened), isGroupIncludedInFile(.recentlyOpened) {
|
||||
RecentlyOpenedImporter(json: groupJSON(.recentlyOpened)).performImport()
|
||||
}
|
||||
|
||||
if importExportModel.isGroupSelected(.otherData), isGroupIncludedInFile(.otherData) {
|
||||
OtherDataSettingsGroupImporter(json: groupJSON(.otherData)).performImport()
|
||||
}
|
||||
}
|
||||
|
||||
var json: JSON? {
|
||||
if let fileContents = try? Data(contentsOf: url),
|
||||
let json = try? JSON(data: fileContents)
|
||||
{
|
||||
return json
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var metadataBuild: String? {
|
||||
if let build = json?.dictionaryValue["metadata"]?.dictionaryValue["build"]?.string {
|
||||
return build
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var metadataPlatform: String? {
|
||||
if let platform = json?.dictionaryValue["metadata"]?.dictionaryValue["platform"]?.string {
|
||||
return platform
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var metadataDate: String? {
|
||||
if let timestamp = json?.dictionaryValue["metadata"]?.dictionaryValue["timestamp"]?.doubleValue {
|
||||
let date = Date(timeIntervalSince1970: timestamp)
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var dateFormatter: DateFormatter {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .long
|
||||
formatter.timeStyle = .medium
|
||||
|
||||
return formatter
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct AdvancedSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let showPlayNowInBackendContextMenu = json["showPlayNowInBackendContextMenu"].bool {
|
||||
Defaults[.showPlayNowInBackendContextMenu] = showPlayNowInBackendContextMenu
|
||||
}
|
||||
|
||||
if let showMPVPlaybackStats = json["showMPVPlaybackStats"].bool {
|
||||
Defaults[.showMPVPlaybackStats] = showMPVPlaybackStats
|
||||
}
|
||||
|
||||
if let mpvEnableLogging = json["mpvEnableLogging"].bool {
|
||||
Defaults[.mpvEnableLogging] = mpvEnableLogging
|
||||
}
|
||||
|
||||
if let mpvCacheSecs = json["mpvCacheSecs"].string {
|
||||
Defaults[.mpvCacheSecs] = mpvCacheSecs
|
||||
}
|
||||
|
||||
if let mpvCachePauseWait = json["mpvCachePauseWait"].string {
|
||||
Defaults[.mpvCachePauseWait] = mpvCachePauseWait
|
||||
}
|
||||
|
||||
if let showCacheStatus = json["showCacheStatus"].bool {
|
||||
Defaults[.showCacheStatus] = showCacheStatus
|
||||
}
|
||||
|
||||
if let feedCacheSize = json["feedCacheSize"].string {
|
||||
Defaults[.feedCacheSize] = feedCacheSize
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct BrowsingSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let showHome = json["showHome"].bool {
|
||||
Defaults[.showHome] = showHome
|
||||
}
|
||||
|
||||
if let showOpenActionsInHome = json["showOpenActionsInHome"].bool {
|
||||
Defaults[.showOpenActionsInHome] = showOpenActionsInHome
|
||||
}
|
||||
|
||||
if let showQueueInHome = json["showQueueInHome"].bool {
|
||||
Defaults[.showQueueInHome] = showQueueInHome
|
||||
}
|
||||
|
||||
if let showFavoritesInHome = json["showFavoritesInHome"].bool {
|
||||
Defaults[.showFavoritesInHome] = showFavoritesInHome
|
||||
}
|
||||
|
||||
if let favorites = json["favorites"].array {
|
||||
favorites.forEach { favoriteJSON in
|
||||
if let jsonString = favoriteJSON.rawString(options: []),
|
||||
let item = FavoriteItem.bridge.deserialize(jsonString)
|
||||
{
|
||||
FavoritesModel.shared.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let widgetsFavorites = json["widgetsSettings"].array {
|
||||
widgetsFavorites.forEach { widgetJSON in
|
||||
let dict = widgetJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
if let item = WidgetSettingsBridge().deserialize(dict) {
|
||||
FavoritesModel.shared.updateWidgetSettings(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let startupSectionString = json["startupSection"].string,
|
||||
let startupSection = StartupSection(rawValue: startupSectionString)
|
||||
{
|
||||
Defaults[.startupSection] = startupSection
|
||||
}
|
||||
|
||||
if let visibleSections = json["visibleSections"].array {
|
||||
let sections = visibleSections.compactMap { visibleSectionJSON in
|
||||
if let visibleSectionString = visibleSectionJSON.rawString(options: []),
|
||||
let section = VisibleSection(rawValue: visibleSectionString)
|
||||
{
|
||||
return section
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Defaults[.visibleSections] = Set(sections)
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
if let showOpenActionsToolbarItem = json["showOpenActionsToolbarItem"].bool {
|
||||
Defaults[.showOpenActionsToolbarItem] = showOpenActionsToolbarItem
|
||||
}
|
||||
|
||||
if let lockPortraitWhenBrowsing = json["lockPortraitWhenBrowsing"].bool {
|
||||
Defaults[.lockPortraitWhenBrowsing] = lockPortraitWhenBrowsing
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !os(tvOS)
|
||||
if let accountPickerDisplaysUsername = json["accountPickerDisplaysUsername"].bool {
|
||||
Defaults[.accountPickerDisplaysUsername] = accountPickerDisplaysUsername
|
||||
}
|
||||
#endif
|
||||
|
||||
if let accountPickerDisplaysAnonymousAccounts = json["accountPickerDisplaysAnonymousAccounts"].bool {
|
||||
Defaults[.accountPickerDisplaysAnonymousAccounts] = accountPickerDisplaysAnonymousAccounts
|
||||
}
|
||||
|
||||
if let showUnwatchedFeedBadges = json["showUnwatchedFeedBadges"].bool {
|
||||
Defaults[.showUnwatchedFeedBadges] = showUnwatchedFeedBadges
|
||||
}
|
||||
|
||||
if let expandChannelDescription = json["expandChannelDescription"].bool {
|
||||
Defaults[.expandChannelDescription] = expandChannelDescription
|
||||
}
|
||||
|
||||
if let keepChannelsWithUnwatchedFeedOnTop = json["keepChannelsWithUnwatchedFeedOnTop"].bool {
|
||||
Defaults[.keepChannelsWithUnwatchedFeedOnTop] = keepChannelsWithUnwatchedFeedOnTop
|
||||
}
|
||||
|
||||
if let showChannelAvatarInChannelsLists = json["showChannelAvatarInChannelsLists"].bool {
|
||||
Defaults[.showChannelAvatarInChannelsLists] = showChannelAvatarInChannelsLists
|
||||
}
|
||||
|
||||
if let showChannelAvatarInVideosListing = json["showChannelAvatarInVideosListing"].bool {
|
||||
Defaults[.showChannelAvatarInVideosListing] = showChannelAvatarInVideosListing
|
||||
}
|
||||
|
||||
if let playerButtonSingleTapGestureString = json["playerButtonSingleTapGesture"].string,
|
||||
let playerButtonSingleTapGesture = PlayerTapGestureAction(rawValue: playerButtonSingleTapGestureString)
|
||||
{
|
||||
Defaults[.playerButtonSingleTapGesture] = playerButtonSingleTapGesture
|
||||
}
|
||||
|
||||
if let playerButtonDoubleTapGestureString = json["playerButtonDoubleTapGesture"].string,
|
||||
let playerButtonDoubleTapGesture = PlayerTapGestureAction(rawValue: playerButtonDoubleTapGestureString)
|
||||
{
|
||||
Defaults[.playerButtonDoubleTapGesture] = playerButtonDoubleTapGesture
|
||||
}
|
||||
|
||||
if let playerButtonShowsControlButtonsWhenMinimized = json["playerButtonShowsControlButtonsWhenMinimized"].bool {
|
||||
Defaults[.playerButtonShowsControlButtonsWhenMinimized] = playerButtonShowsControlButtonsWhenMinimized
|
||||
}
|
||||
|
||||
if let playerButtonIsExpanded = json["playerButtonIsExpanded"].bool {
|
||||
Defaults[.playerButtonIsExpanded] = playerButtonIsExpanded
|
||||
}
|
||||
|
||||
if let playerBarMaxWidth = json["playerBarMaxWidth"].string {
|
||||
Defaults[.playerBarMaxWidth] = playerBarMaxWidth
|
||||
}
|
||||
|
||||
if let channelOnThumbnail = json["channelOnThumbnail"].bool {
|
||||
Defaults[.channelOnThumbnail] = channelOnThumbnail
|
||||
}
|
||||
|
||||
if let timeOnThumbnail = json["timeOnThumbnail"].bool {
|
||||
Defaults[.timeOnThumbnail] = timeOnThumbnail
|
||||
}
|
||||
|
||||
if let roundedThumbnails = json["roundedThumbnails"].bool {
|
||||
Defaults[.roundedThumbnails] = roundedThumbnails
|
||||
}
|
||||
|
||||
if let thumbnailsQualityString = json["thumbnailsQuality"].string,
|
||||
let thumbnailsQuality = ThumbnailsQuality(rawValue: thumbnailsQualityString)
|
||||
{
|
||||
Defaults[.thumbnailsQuality] = thumbnailsQuality
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct ConstrolsSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let avPlayerUsesSystemControls = json["avPlayerUsesSystemControls"].bool {
|
||||
Defaults[.avPlayerUsesSystemControls] = avPlayerUsesSystemControls
|
||||
}
|
||||
|
||||
if let horizontalPlayerGestureEnabled = json["horizontalPlayerGestureEnabled"].bool {
|
||||
Defaults[.horizontalPlayerGestureEnabled] = horizontalPlayerGestureEnabled
|
||||
}
|
||||
|
||||
if let seekGestureSensitivity = json["seekGestureSensitivity"].double {
|
||||
Defaults[.seekGestureSensitivity] = seekGestureSensitivity
|
||||
}
|
||||
|
||||
if let seekGestureSpeed = json["seekGestureSpeed"].double {
|
||||
Defaults[.seekGestureSpeed] = seekGestureSpeed
|
||||
}
|
||||
|
||||
if let playerControlsLayoutString = json["playerControlsLayout"].string,
|
||||
let playerControlsLayout = PlayerControlsLayout(rawValue: playerControlsLayoutString)
|
||||
{
|
||||
Defaults[.playerControlsLayout] = playerControlsLayout
|
||||
}
|
||||
|
||||
if let fullScreenPlayerControlsLayoutString = json["fullScreenPlayerControlsLayout"].string,
|
||||
let fullScreenPlayerControlsLayout = PlayerControlsLayout(rawValue: fullScreenPlayerControlsLayoutString)
|
||||
{
|
||||
Defaults[.fullScreenPlayerControlsLayout] = fullScreenPlayerControlsLayout
|
||||
}
|
||||
|
||||
if let systemControlsCommandsString = json["systemControlsCommands"].string,
|
||||
let systemControlsCommands = SystemControlsCommands(rawValue: systemControlsCommandsString)
|
||||
{
|
||||
Defaults[.systemControlsCommands] = systemControlsCommands
|
||||
}
|
||||
|
||||
if let buttonBackwardSeekDuration = json["buttonBackwardSeekDuration"].string {
|
||||
Defaults[.buttonBackwardSeekDuration] = buttonBackwardSeekDuration
|
||||
}
|
||||
|
||||
if let buttonForwardSeekDuration = json["buttonForwardSeekDuration"].string {
|
||||
Defaults[.buttonForwardSeekDuration] = buttonForwardSeekDuration
|
||||
}
|
||||
|
||||
if let gestureBackwardSeekDuration = json["gestureBackwardSeekDuration"].string {
|
||||
Defaults[.gestureBackwardSeekDuration] = gestureBackwardSeekDuration
|
||||
}
|
||||
|
||||
if let gestureForwardSeekDuration = json["gestureForwardSeekDuration"].string {
|
||||
Defaults[.gestureForwardSeekDuration] = gestureForwardSeekDuration
|
||||
}
|
||||
|
||||
if let systemControlsSeekDuration = json["systemControlsSeekDuration"].string {
|
||||
Defaults[.systemControlsSeekDuration] = systemControlsSeekDuration
|
||||
}
|
||||
|
||||
if let playerControlsSettingsEnabled = json["playerControlsSettingsEnabled"].bool {
|
||||
Defaults[.playerControlsSettingsEnabled] = playerControlsSettingsEnabled
|
||||
}
|
||||
|
||||
if let playerControlsCloseEnabled = json["playerControlsCloseEnabled"].bool {
|
||||
Defaults[.playerControlsCloseEnabled] = playerControlsCloseEnabled
|
||||
}
|
||||
|
||||
if let playerControlsRestartEnabled = json["playerControlsRestartEnabled"].bool {
|
||||
Defaults[.playerControlsRestartEnabled] = playerControlsRestartEnabled
|
||||
}
|
||||
|
||||
if let playerControlsAdvanceToNextEnabled = json["playerControlsAdvanceToNextEnabled"].bool {
|
||||
Defaults[.playerControlsAdvanceToNextEnabled] = playerControlsAdvanceToNextEnabled
|
||||
}
|
||||
|
||||
if let playerControlsPlaybackModeEnabled = json["playerControlsPlaybackModeEnabled"].bool {
|
||||
Defaults[.playerControlsPlaybackModeEnabled] = playerControlsPlaybackModeEnabled
|
||||
}
|
||||
|
||||
if let playerControlsMusicModeEnabled = json["playerControlsMusicModeEnabled"].bool {
|
||||
Defaults[.playerControlsMusicModeEnabled] = playerControlsMusicModeEnabled
|
||||
}
|
||||
|
||||
if let playerActionsButtonLabelStyleString = json["playerActionsButtonLabelStyle"].string,
|
||||
let playerActionsButtonLabelStyle = ButtonLabelStyle(rawValue: playerActionsButtonLabelStyleString)
|
||||
{
|
||||
Defaults[.playerActionsButtonLabelStyle] = playerActionsButtonLabelStyle
|
||||
}
|
||||
|
||||
if let actionButtonShareEnabled = json["actionButtonShareEnabled"].bool {
|
||||
Defaults[.actionButtonShareEnabled] = actionButtonShareEnabled
|
||||
}
|
||||
|
||||
if let actionButtonAddToPlaylistEnabled = json["actionButtonAddToPlaylistEnabled"].bool {
|
||||
Defaults[.actionButtonAddToPlaylistEnabled] = actionButtonAddToPlaylistEnabled
|
||||
}
|
||||
|
||||
if let actionButtonSubscribeEnabled = json["actionButtonSubscribeEnabled"].bool {
|
||||
Defaults[.actionButtonSubscribeEnabled] = actionButtonSubscribeEnabled
|
||||
}
|
||||
|
||||
if let actionButtonSettingsEnabled = json["actionButtonSettingsEnabled"].bool {
|
||||
Defaults[.actionButtonSettingsEnabled] = actionButtonSettingsEnabled
|
||||
}
|
||||
|
||||
if let actionButtonHideEnabled = json["actionButtonHideEnabled"].bool {
|
||||
Defaults[.actionButtonHideEnabled] = actionButtonHideEnabled
|
||||
}
|
||||
|
||||
if let actionButtonCloseEnabled = json["actionButtonCloseEnabled"].bool {
|
||||
Defaults[.actionButtonCloseEnabled] = actionButtonCloseEnabled
|
||||
}
|
||||
|
||||
if let actionButtonFullScreenEnabled = json["actionButtonFullScreenEnabled"].bool {
|
||||
Defaults[.actionButtonFullScreenEnabled] = actionButtonFullScreenEnabled
|
||||
}
|
||||
|
||||
if let actionButtonPipEnabled = json["actionButtonPipEnabled"].bool {
|
||||
Defaults[.actionButtonPipEnabled] = actionButtonPipEnabled
|
||||
}
|
||||
|
||||
if let actionButtonLockOrientationEnabled = json["actionButtonLockOrientationEnabled"].bool {
|
||||
Defaults[.actionButtonLockOrientationEnabled] = actionButtonLockOrientationEnabled
|
||||
}
|
||||
|
||||
if let actionButtonRestartEnabled = json["actionButtonRestartEnabled"].bool {
|
||||
Defaults[.actionButtonRestartEnabled] = actionButtonRestartEnabled
|
||||
}
|
||||
|
||||
if let actionButtonAdvanceToNextItemEnabled = json["actionButtonAdvanceToNextItemEnabled"].bool {
|
||||
Defaults[.actionButtonAdvanceToNextItemEnabled] = actionButtonAdvanceToNextItemEnabled
|
||||
}
|
||||
|
||||
if let actionButtonMusicModeEnabled = json["actionButtonMusicModeEnabled"].bool {
|
||||
Defaults[.actionButtonMusicModeEnabled] = actionButtonMusicModeEnabled
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct HistorySettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let saveRecents = json["saveRecents"].bool {
|
||||
Defaults[.saveRecents] = saveRecents
|
||||
}
|
||||
|
||||
if let saveHistory = json["saveHistory"].bool {
|
||||
Defaults[.saveHistory] = saveHistory
|
||||
}
|
||||
|
||||
if let showWatchingProgress = json["showWatchingProgress"].bool {
|
||||
Defaults[.showWatchingProgress] = showWatchingProgress
|
||||
}
|
||||
|
||||
if let saveLastPlayed = json["saveLastPlayed"].bool {
|
||||
Defaults[.saveLastPlayed] = saveLastPlayed
|
||||
}
|
||||
|
||||
if let watchedVideoPlayNowBehaviorString = json["watchedVideoPlayNowBehavior"].string,
|
||||
let watchedVideoPlayNowBehavior = WatchedVideoPlayNowBehavior(rawValue: watchedVideoPlayNowBehaviorString)
|
||||
{
|
||||
Defaults[.watchedVideoPlayNowBehavior] = watchedVideoPlayNowBehavior
|
||||
}
|
||||
|
||||
if let watchedThreshold = json["watchedThreshold"].int {
|
||||
Defaults[.watchedThreshold] = watchedThreshold
|
||||
}
|
||||
|
||||
if let resetWatchedStatusOnPlaying = json["resetWatchedStatusOnPlaying"].bool {
|
||||
Defaults[.resetWatchedStatusOnPlaying] = resetWatchedStatusOnPlaying
|
||||
}
|
||||
|
||||
if let watchedVideoStyleString = json["watchedVideoStyle"].string,
|
||||
let watchedVideoStyle = WatchedVideoStyle(rawValue: watchedVideoStyleString)
|
||||
{
|
||||
Defaults[.watchedVideoStyle] = watchedVideoStyle
|
||||
}
|
||||
|
||||
if let watchedVideoBadgeColorString = json["watchedVideoBadgeColor"].string,
|
||||
let watchedVideoBadgeColor = WatchedVideoBadgeColor(rawValue: watchedVideoBadgeColorString)
|
||||
{
|
||||
Defaults[.watchedVideoBadgeColor] = watchedVideoBadgeColor
|
||||
}
|
||||
|
||||
if let showToggleWatchedStatusButton = json["showToggleWatchedStatusButton"].bool {
|
||||
Defaults[.showToggleWatchedStatusButton] = showToggleWatchedStatusButton
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct LocationsSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
var includePublicLocations = true
|
||||
var includedInstancesIDs = Set<Instance.ID>()
|
||||
var includedAccountsIDs = Set<Account.ID>()
|
||||
var includedAccountsPasswords = [Account.ID: String]()
|
||||
|
||||
init(
|
||||
json: JSON,
|
||||
includePublicLocations: Bool = true,
|
||||
includedInstancesIDs: Set<Instance.ID> = [],
|
||||
includedAccountsIDs: Set<Account.ID> = [],
|
||||
includedAccountsPasswords: [Account.ID: String] = [:]
|
||||
) {
|
||||
self.json = json
|
||||
self.includePublicLocations = includePublicLocations
|
||||
self.includedInstancesIDs = includedInstancesIDs
|
||||
self.includedAccountsIDs = includedAccountsIDs
|
||||
self.includedAccountsPasswords = includedAccountsPasswords
|
||||
}
|
||||
|
||||
var instances: [Instance] {
|
||||
if let instances = json["instances"].array {
|
||||
return instances.compactMap { instanceJSON in
|
||||
let dict = instanceJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
return InstancesBridge().deserialize(dict)
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
if let accounts = json["accounts"].array {
|
||||
return accounts.compactMap { accountJSON in
|
||||
let dict = accountJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
return AccountsBridge().deserialize(dict)
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
func performImport() {
|
||||
if includePublicLocations {
|
||||
Defaults[.instancesManifest] = json["instancesManifest"].string ?? ""
|
||||
Defaults[.countryOfPublicInstances] = json["countryOfPublicInstances"].string ?? ""
|
||||
}
|
||||
|
||||
instances.filter { includedInstancesIDs.contains($0.id) }.forEach { instance in
|
||||
_ = InstancesModel.shared.insert(id: instance.id, app: instance.app, name: instance.name, url: instance.apiURLString)
|
||||
}
|
||||
|
||||
if let accounts = json["accounts"].array {
|
||||
accounts.forEach { accountJSON in
|
||||
let dict = accountJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
if let account = AccountsBridge().deserialize(dict),
|
||||
includedAccountsIDs.contains(account.id)
|
||||
{
|
||||
var password = account.password
|
||||
if password?.isEmpty ?? true {
|
||||
password = includedAccountsPasswords[account.id]
|
||||
}
|
||||
if let password,
|
||||
!password.isEmpty,
|
||||
let instanceID = account.instanceID,
|
||||
let instance = InstancesModel.shared.find(instanceID)
|
||||
{
|
||||
if !instance.accounts.contains(where: { instanceAccount in
|
||||
let (username, _) = instanceAccount.credentials
|
||||
return username == account.username
|
||||
}) {
|
||||
_ = AccountsModel.add(instance: instance, id: account.id, name: account.name, username: account.username, password: password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct OtherDataSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let lastAccountID = json["lastAccountID"].string {
|
||||
Defaults[.lastAccountID] = lastAccountID
|
||||
}
|
||||
|
||||
if let lastInstanceID = json["lastInstanceID"].string {
|
||||
Defaults[.lastInstanceID] = lastInstanceID
|
||||
}
|
||||
|
||||
if let playerRate = json["playerRate"].double {
|
||||
Defaults[.playerRate] = playerRate
|
||||
}
|
||||
|
||||
if let trendingCategoryString = json["trendingCategory"].string,
|
||||
let trendingCategory = TrendingCategory(rawValue: trendingCategoryString)
|
||||
{
|
||||
Defaults[.trendingCategory] = trendingCategory
|
||||
}
|
||||
|
||||
if let trendingCountryString = json["trendingCountry"].string,
|
||||
let trendingCountry = Country(rawValue: trendingCountryString)
|
||||
{
|
||||
Defaults[.trendingCountry] = trendingCountry
|
||||
}
|
||||
|
||||
if let subscriptionsViewPageString = json["subscriptionsViewPage"].string,
|
||||
let subscriptionsViewPage = SubscriptionsView.Page(rawValue: subscriptionsViewPageString)
|
||||
{
|
||||
Defaults[.subscriptionsViewPage] = subscriptionsViewPage
|
||||
}
|
||||
|
||||
if let subscriptionsListingStyle = json["subscriptionsListingStyle"].string {
|
||||
Defaults[.subscriptionsListingStyle] = ListingStyle(rawValue: subscriptionsListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let popularListingStyle = json["popularListingStyle"].string {
|
||||
Defaults[.popularListingStyle] = ListingStyle(rawValue: popularListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let trendingListingStyle = json["trendingListingStyle"].string {
|
||||
Defaults[.trendingListingStyle] = ListingStyle(rawValue: trendingListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let playlistListingStyle = json["playlistListingStyle"].string {
|
||||
Defaults[.playlistListingStyle] = ListingStyle(rawValue: playlistListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let channelPlaylistListingStyle = json["channelPlaylistListingStyle"].string {
|
||||
Defaults[.channelPlaylistListingStyle] = ListingStyle(rawValue: channelPlaylistListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let searchListingStyle = json["searchListingStyle"].string {
|
||||
Defaults[.searchListingStyle] = ListingStyle(rawValue: searchListingStyle) ?? .list
|
||||
}
|
||||
|
||||
if let hideShorts = json["hideShorts"].bool {
|
||||
Defaults[.hideShorts] = hideShorts
|
||||
}
|
||||
|
||||
if let hideWatched = json["hideWatched"].bool {
|
||||
Defaults[.hideWatched] = hideWatched
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct PlayerSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let playerInstanceID = json["playerInstanceID"].string {
|
||||
Defaults[.playerInstanceID] = playerInstanceID
|
||||
}
|
||||
|
||||
if let pauseOnHidingPlayer = json["pauseOnHidingPlayer"].bool {
|
||||
Defaults[.pauseOnHidingPlayer] = pauseOnHidingPlayer
|
||||
}
|
||||
|
||||
if let closeVideoOnEOF = json["closeVideoOnEOF"].bool {
|
||||
Defaults[.closeVideoOnEOF] = closeVideoOnEOF
|
||||
}
|
||||
|
||||
if let expandVideoDescription = json["expandVideoDescription"].bool {
|
||||
Defaults[.expandVideoDescription] = expandVideoDescription
|
||||
}
|
||||
|
||||
if let collapsedLinesDescription = json["collapsedLinesDescription"].int {
|
||||
Defaults[.collapsedLinesDescription] = collapsedLinesDescription
|
||||
}
|
||||
|
||||
if let showChapters = json["showChapters"].bool {
|
||||
Defaults[.showChapters] = showChapters
|
||||
}
|
||||
|
||||
if let expandChapters = json["expandChapters"].bool {
|
||||
Defaults[.expandChapters] = expandChapters
|
||||
}
|
||||
|
||||
if let showRelated = json["showRelated"].bool {
|
||||
Defaults[.showRelated] = showRelated
|
||||
}
|
||||
|
||||
if let showInspectorString = json["showInspector"].string,
|
||||
let showInspector = ShowInspectorSetting(rawValue: showInspectorString)
|
||||
{
|
||||
Defaults[.showInspector] = showInspector
|
||||
}
|
||||
|
||||
if let playerSidebarString = json["playerSidebar"].string,
|
||||
let playerSidebar = PlayerSidebarSetting(rawValue: playerSidebarString)
|
||||
{
|
||||
Defaults[.playerSidebar] = playerSidebar
|
||||
}
|
||||
|
||||
if let showKeywords = json["showKeywords"].bool {
|
||||
Defaults[.showKeywords] = showKeywords
|
||||
}
|
||||
|
||||
if let enableReturnYouTubeDislike = json["enableReturnYouTubeDislike"].bool {
|
||||
Defaults[.enableReturnYouTubeDislike] = enableReturnYouTubeDislike
|
||||
}
|
||||
|
||||
if let closePiPOnNavigation = json["closePiPOnNavigation"].bool {
|
||||
Defaults[.closePiPOnNavigation] = closePiPOnNavigation
|
||||
}
|
||||
|
||||
if let closePiPOnOpeningPlayer = json["closePiPOnOpeningPlayer"].bool {
|
||||
Defaults[.closePiPOnOpeningPlayer] = closePiPOnOpeningPlayer
|
||||
}
|
||||
|
||||
if let closePlayerOnOpeningPiP = json["closePlayerOnOpeningPiP"].bool {
|
||||
Defaults[.closePlayerOnOpeningPiP] = closePlayerOnOpeningPiP
|
||||
}
|
||||
|
||||
#if !os(macOS)
|
||||
if let pauseOnEnteringBackground = json["pauseOnEnteringBackground"].bool {
|
||||
Defaults[.pauseOnEnteringBackground] = pauseOnEnteringBackground
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !os(tvOS)
|
||||
if let showScrollToTopInComments = json["showScrollToTopInComments"].bool {
|
||||
Defaults[.showScrollToTopInComments] = showScrollToTopInComments
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
if let honorSystemOrientationLock = json["honorSystemOrientationLock"].bool {
|
||||
Defaults[.honorSystemOrientationLock] = honorSystemOrientationLock
|
||||
}
|
||||
|
||||
if let enterFullscreenInLandscape = json["enterFullscreenInLandscape"].bool {
|
||||
Defaults[.enterFullscreenInLandscape] = enterFullscreenInLandscape
|
||||
}
|
||||
|
||||
if let rotateToLandscapeOnEnterFullScreenString = json["rotateToLandscapeOnEnterFullScreen"].string,
|
||||
let rotateToLandscapeOnEnterFullScreen = FullScreenRotationSetting(rawValue: rotateToLandscapeOnEnterFullScreenString)
|
||||
{
|
||||
Defaults[.rotateToLandscapeOnEnterFullScreen] = rotateToLandscapeOnEnterFullScreen
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct QualitySettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let batteryCellularProfileString = json["batteryCellularProfile"].string {
|
||||
Defaults[.batteryCellularProfile] = batteryCellularProfileString
|
||||
}
|
||||
|
||||
if let batteryNonCellularProfileString = json["batteryNonCellularProfile"].string {
|
||||
Defaults[.batteryNonCellularProfile] = batteryNonCellularProfileString
|
||||
}
|
||||
|
||||
if let chargingCellularProfileString = json["chargingCellularProfile"].string {
|
||||
Defaults[.chargingCellularProfile] = chargingCellularProfileString
|
||||
}
|
||||
|
||||
if let chargingNonCellularProfileString = json["chargingNonCellularProfile"].string {
|
||||
Defaults[.chargingNonCellularProfile] = chargingNonCellularProfileString
|
||||
}
|
||||
|
||||
if let forceAVPlayerForLiveStreams = json["forceAVPlayerForLiveStreams"].bool {
|
||||
Defaults[.forceAVPlayerForLiveStreams] = forceAVPlayerForLiveStreams
|
||||
}
|
||||
|
||||
if let qualityProfiles = json["qualityProfiles"].array {
|
||||
qualityProfiles.forEach { qualityProfileJSON in
|
||||
let dict = qualityProfileJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
if let item = QualityProfileBridge().deserialize(dict) {
|
||||
QualityProfilesModel.shared.update(item, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct RecentlyOpenedImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let recentlyOpened = json["recentlyOpened"].array {
|
||||
recentlyOpened.forEach { recentlyOpenedJSON in
|
||||
let dict = recentlyOpenedJSON.dictionaryValue.mapValues { json in json.stringValue }
|
||||
if let item = RecentItemBridge().deserialize(dict) {
|
||||
RecentsModel.shared.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import Defaults
|
||||
import SwiftyJSON
|
||||
|
||||
struct SponsorBlockSettingsGroupImporter {
|
||||
var json: JSON
|
||||
|
||||
func performImport() {
|
||||
if let sponsorBlockInstance = json["sponsorBlockInstance"].string {
|
||||
Defaults[.sponsorBlockInstance] = sponsorBlockInstance
|
||||
}
|
||||
|
||||
if let sponsorBlockCategories = json["sponsorBlockCategories"].array {
|
||||
Defaults[.sponsorBlockCategories] = Set(sponsorBlockCategories.compactMap { $0.string })
|
||||
}
|
||||
}
|
||||
}
|
@ -107,6 +107,10 @@ final class NavigationModel: ObservableObject {
|
||||
|
||||
@Published var presentingFileImporter = false
|
||||
|
||||
@Published var presentingSettingsImportSheet = false
|
||||
@Published var presentingSettingsFileImporter = false
|
||||
@Published var settingsImportURL: URL?
|
||||
|
||||
func openChannel(_ channel: Channel, navigationStyle: NavigationStyle) {
|
||||
guard channel.id != Video.fixtureChannelID else {
|
||||
return
|
||||
@ -269,6 +273,8 @@ final class NavigationModel: ObservableObject {
|
||||
presentingChannel = false
|
||||
presentingPlaylist = false
|
||||
presentingOpenVideos = false
|
||||
presentingFileImporter = false
|
||||
presentingSettingsImportSheet = false
|
||||
}
|
||||
|
||||
func hideKeyboard() {
|
||||
@ -279,8 +285,9 @@ final class NavigationModel: ObservableObject {
|
||||
|
||||
func presentAlert(title: String, message: String? = nil) {
|
||||
let message = message.isNil ? nil : Text(message!)
|
||||
alert = Alert(title: Text(title), message: message)
|
||||
presentingAlert = true
|
||||
let alert = Alert(title: Text(title), message: message)
|
||||
|
||||
presentAlert(alert)
|
||||
}
|
||||
|
||||
func presentRequestErrorAlert(_ error: RequestError) {
|
||||
@ -289,6 +296,11 @@ final class NavigationModel: ObservableObject {
|
||||
}
|
||||
|
||||
func presentAlert(_ alert: Alert) {
|
||||
guard !presentingSettings else {
|
||||
SettingsModel.shared.presentAlert(alert)
|
||||
return
|
||||
}
|
||||
|
||||
self.alert = alert
|
||||
presentingAlert = true
|
||||
}
|
||||
@ -311,6 +323,16 @@ final class NavigationModel: ObservableObject {
|
||||
print("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func presentSettingsImportSheet(_ url: URL, forceSettings: Bool = false) {
|
||||
guard !presentingSettings, !forceSettings else {
|
||||
ImportExportSettingsModel.shared.reset()
|
||||
SettingsModel.shared.presentSettingsImportSheet(url)
|
||||
return
|
||||
}
|
||||
settingsImportURL = url
|
||||
presentingSettingsImportSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
typealias TabSelection = NavigationModel.TabSelection
|
||||
|
@ -7,6 +7,9 @@ final class SettingsModel: ObservableObject {
|
||||
@Published var presentingAlert = false
|
||||
@Published var alert = Alert(title: Text("Error"))
|
||||
|
||||
@Published var presentingSettingsImportSheet = false
|
||||
@Published var settingsImportURL: URL?
|
||||
|
||||
func presentAlert(title: String, message: String? = nil) {
|
||||
let message = message.isNil ? nil : Text(message!)
|
||||
alert = Alert(title: Text(title), message: message)
|
||||
@ -17,4 +20,9 @@ final class SettingsModel: ObservableObject {
|
||||
self.alert = alert
|
||||
presentingAlert = true
|
||||
}
|
||||
|
||||
func presentSettingsImportSheet(_ url: URL) {
|
||||
settingsImportURL = url
|
||||
presentingSettingsImportSheet = true
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,26 @@ enum Constants {
|
||||
#endif
|
||||
}
|
||||
|
||||
static var deviceName: String {
|
||||
#if os(macOS)
|
||||
Host().localizedName ?? "Mac"
|
||||
#else
|
||||
UIDevice.current.name
|
||||
#endif
|
||||
}
|
||||
|
||||
static var platform: String {
|
||||
#if os(macOS)
|
||||
"macOS"
|
||||
#elseif os(iOS)
|
||||
"iOS"
|
||||
#elseif os(tvOS)
|
||||
"tvOS"
|
||||
#else
|
||||
"unknown"
|
||||
#endif
|
||||
}
|
||||
|
||||
static func seekIcon(_ type: String, _ interval: TimeInterval) -> String {
|
||||
let interval = Int(interval)
|
||||
let allVersions = [10, 15, 30, 45, 60, 75, 90]
|
||||
|
@ -6,37 +6,22 @@ import SwiftUI
|
||||
#endif
|
||||
|
||||
extension Defaults.Keys {
|
||||
static let instancesManifest = Key<String>("instancesManifest", default: "")
|
||||
static let countryOfPublicInstances = Key<String?>("countryOfPublicInstances")
|
||||
|
||||
static let instances = Key<[Instance]>("instances", default: [])
|
||||
static let accounts = Key<[Account]>("accounts", default: [])
|
||||
static let lastAccountID = Key<Account.ID?>("lastAccountID")
|
||||
static let lastInstanceID = Key<Instance.ID?>("lastInstanceID")
|
||||
static let lastUsedPlaylistID = Key<Playlist.ID?>("lastPlaylistID")
|
||||
static let lastAccountIsPublic = Key<Bool>("lastAccountIsPublic", default: false)
|
||||
|
||||
static let sponsorBlockInstance = Key<String>("sponsorBlockInstance", default: "https://sponsor.ajay.app")
|
||||
static let sponsorBlockCategories = Key<Set<String>>("sponsorBlockCategories", default: Set(SponsorBlockAPI.categories))
|
||||
|
||||
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
||||
// MARK: GROUP - Browsing
|
||||
|
||||
static let showHome = Key<Bool>("showHome", default: true)
|
||||
static let showOpenActionsInHome = Key<Bool>("showOpenActionsInHome", default: true)
|
||||
static let showQueueInHome = Key<Bool>("showQueueInHome", default: true)
|
||||
static let showOpenActionsToolbarItem = Key<Bool>("showOpenActionsToolbarItem", default: false)
|
||||
static let showFavoritesInHome = Key<Bool>("showFavoritesInHome", default: true)
|
||||
static let favorites = Key<[FavoriteItem]>("favorites", default: [])
|
||||
static let widgetsSettings = Key<[WidgetSettings]>("widgetsSettings", default: [])
|
||||
static let startupSection = Key<StartupSection>("startupSection", default: .home)
|
||||
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.subscriptions, .trending, .playlists])
|
||||
|
||||
static let showOpenActionsToolbarItem = Key<Bool>("showOpenActionsToolbarItem", default: false)
|
||||
#if os(iOS)
|
||||
static let showDocuments = Key<Bool>("showDocuments", default: false)
|
||||
static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||
#endif
|
||||
static let homeHistoryItems = Key<Int>("homeHistoryItems", default: 10)
|
||||
static let favorites = Key<[FavoriteItem]>("favorites", default: [])
|
||||
|
||||
static let playerButtonSingleTapGesture = Key<PlayerTapGestureAction>("playerButtonSingleTapGesture", default: .togglePlayer)
|
||||
static let playerButtonDoubleTapGesture = Key<PlayerTapGestureAction>("playerButtonDoubleTapGesture", default: .nothing)
|
||||
static let playerButtonShowsControlButtonsWhenMinimized = Key<Bool>("playerButtonShowsControlButtonsWhenMinimized", default: false)
|
||||
static let playerButtonIsExpanded = Key<Bool>("playerButtonIsExpanded", default: false)
|
||||
static let playerBarMaxWidth = Key<String>("playerBarMaxWidth", default: "600")
|
||||
|
||||
#if !os(tvOS)
|
||||
#if os(macOS)
|
||||
@ -46,21 +31,146 @@ extension Defaults.Keys {
|
||||
#endif
|
||||
static let accountPickerDisplaysUsername = Key<Bool>("accountPickerDisplaysUsername", default: accountPickerDisplaysUsernameDefault)
|
||||
#endif
|
||||
|
||||
static let accountPickerDisplaysAnonymousAccounts = Key<Bool>("accountPickerDisplaysAnonymousAccounts", default: true)
|
||||
#if os(iOS)
|
||||
static let lockPortraitWhenBrowsing = Key<Bool>("lockPortraitWhenBrowsing", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||
#endif
|
||||
static let showUnwatchedFeedBadges = Key<Bool>("showUnwatchedFeedBadges", default: false)
|
||||
static let keepChannelsWithUnwatchedFeedOnTop = Key<Bool>("keepChannelsWithUnwatchedFeedOnTop", default: true)
|
||||
static let showToggleWatchedStatusButton = Key<Bool>("showToggleWatchedStatusButton", default: false)
|
||||
static let expandChannelDescription = Key<Bool>("expandChannelDescription", default: false)
|
||||
|
||||
static let keepChannelsWithUnwatchedFeedOnTop = Key<Bool>("keepChannelsWithUnwatchedFeedOnTop", default: true)
|
||||
static let showChannelAvatarInChannelsLists = Key<Bool>("showChannelAvatarInChannelsLists", default: true)
|
||||
static let showChannelAvatarInVideosListing = Key<Bool>("showChannelAvatarInVideosListing", default: true)
|
||||
|
||||
static let playerButtonSingleTapGesture = Key<PlayerTapGestureAction>("playerButtonSingleTapGesture", default: .togglePlayer)
|
||||
static let playerButtonDoubleTapGesture = Key<PlayerTapGestureAction>("playerButtonDoubleTapGesture", default: .nothing)
|
||||
static let playerButtonShowsControlButtonsWhenMinimized = Key<Bool>("playerButtonShowsControlButtonsWhenMinimized", default: false)
|
||||
static let playerButtonIsExpanded = Key<Bool>("playerButtonIsExpanded", default: false)
|
||||
static let playerBarMaxWidth = Key<String>("playerBarMaxWidth", default: "600")
|
||||
static let channelOnThumbnail = Key<Bool>("channelOnThumbnail", default: false)
|
||||
static let timeOnThumbnail = Key<Bool>("timeOnThumbnail", default: true)
|
||||
static let roundedThumbnails = Key<Bool>("roundedThumbnails", default: true)
|
||||
static let thumbnailsQuality = Key<ThumbnailsQuality>("thumbnailsQuality", default: .highest)
|
||||
|
||||
static let captionsLanguageCode = Key<String?>("captionsLanguageCode")
|
||||
static let activeBackend = Key<PlayerBackendType>("activeBackend", default: .mpv)
|
||||
// MARK: GROUP - Player
|
||||
|
||||
static let playerInstanceID = Key<Instance.ID?>("playerInstance")
|
||||
|
||||
#if os(tvOS)
|
||||
static let pauseOnHidingPlayerDefault = true
|
||||
#else
|
||||
static let pauseOnHidingPlayerDefault = false
|
||||
#endif
|
||||
static let pauseOnHidingPlayer = Key<Bool>("pauseOnHidingPlayer", default: pauseOnHidingPlayerDefault)
|
||||
|
||||
static let closeVideoOnEOF = Key<Bool>("closeVideoOnEOF", default: false)
|
||||
|
||||
#if !os(macOS)
|
||||
static let pauseOnEnteringBackground = Key<Bool>("pauseOnEnteringBackground", default: true)
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
static let expandVideoDescriptionDefault = Constants.isIPad
|
||||
#else
|
||||
static let expandVideoDescriptionDefault = true
|
||||
#endif
|
||||
static let expandVideoDescription = Key<Bool>("expandVideoDescription", default: expandVideoDescriptionDefault)
|
||||
|
||||
static let collapsedLinesDescription = Key<Int>("collapsedLinesDescription", default: 5)
|
||||
|
||||
static let showChapters = Key<Bool>("showChapters", default: true)
|
||||
static let expandChapters = Key<Bool>("expandChapters", default: true)
|
||||
static let showRelated = Key<Bool>("showRelated", default: true)
|
||||
static let showInspector = Key<ShowInspectorSetting>("showInspector", default: .onlyLocal)
|
||||
|
||||
static let playerSidebar = Key<PlayerSidebarSetting>("playerSidebar", default: .defaultValue)
|
||||
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
||||
#if !os(tvOS)
|
||||
static let showScrollToTopInComments = Key<Bool>("showScrollToTopInComments", default: true)
|
||||
#endif
|
||||
static let enableReturnYouTubeDislike = Key<Bool>("enableReturnYouTubeDislike", default: false)
|
||||
|
||||
#if os(iOS)
|
||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
|
||||
"rotateToLandscapeOnEnterFullScreen",
|
||||
default: UIDevice.current.userInterfaceIdiom == .phone ? .landscapeRight : .disabled
|
||||
)
|
||||
#endif
|
||||
|
||||
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
||||
static let closePiPOnOpeningPlayer = Key<Bool>("closePiPOnOpeningPlayer", default: false)
|
||||
static let closePlayerOnOpeningPiP = Key<Bool>("closePlayerOnOpeningPiP", default: false)
|
||||
#if !os(macOS)
|
||||
static let closePiPAndOpenPlayerOnEnteringForeground = Key<Bool>("closePiPAndOpenPlayerOnEnteringForeground", default: false)
|
||||
#endif
|
||||
|
||||
// MARK: GROUP - Controls
|
||||
|
||||
static let avPlayerUsesSystemControls = Key<Bool>("avPlayerUsesSystemControls", default: true)
|
||||
static let horizontalPlayerGestureEnabled = Key<Bool>("horizontalPlayerGestureEnabled", default: true)
|
||||
static let seekGestureSensitivity = Key<Double>("seekGestureSensitivity", default: 30.0)
|
||||
static let seekGestureSpeed = Key<Double>("seekGestureSpeed", default: 0.5)
|
||||
|
||||
#if os(iOS)
|
||||
static let playerControlsLayoutDefault = UIDevice.current.userInterfaceIdiom == .pad ? PlayerControlsLayout.medium : .small
|
||||
static let fullScreenPlayerControlsLayoutDefault = UIDevice.current.userInterfaceIdiom == .pad ? PlayerControlsLayout.medium : .small
|
||||
#elseif os(tvOS)
|
||||
static let playerControlsLayoutDefault = PlayerControlsLayout.tvRegular
|
||||
static let fullScreenPlayerControlsLayoutDefault = PlayerControlsLayout.tvRegular
|
||||
#else
|
||||
static let playerControlsLayoutDefault = PlayerControlsLayout.medium
|
||||
static let fullScreenPlayerControlsLayoutDefault = PlayerControlsLayout.medium
|
||||
#endif
|
||||
|
||||
static let playerControlsLayout = Key<PlayerControlsLayout>("playerControlsLayout", default: playerControlsLayoutDefault)
|
||||
static let fullScreenPlayerControlsLayout = Key<PlayerControlsLayout>("fullScreenPlayerControlsLayout", default: fullScreenPlayerControlsLayoutDefault)
|
||||
|
||||
static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
|
||||
|
||||
static let buttonBackwardSeekDuration = Key<String>("buttonBackwardSeekDuration", default: "10")
|
||||
static let buttonForwardSeekDuration = Key<String>("buttonForwardSeekDuration", default: "10")
|
||||
static let gestureBackwardSeekDuration = Key<String>("gestureBackwardSeekDuration", default: "10")
|
||||
static let gestureForwardSeekDuration = Key<String>("gestureForwardSeekDuration", default: "10")
|
||||
static let systemControlsSeekDuration = Key<String>("systemControlsBackwardSeekDuration", default: "10")
|
||||
|
||||
#if os(iOS)
|
||||
static let playerControlsLockOrientationEnabled = Key<Bool>("playerControlsLockOrientationEnabled", default: true)
|
||||
#endif
|
||||
#if os(tvOS)
|
||||
static let playerControlsSettingsEnabledDefault = true
|
||||
#else
|
||||
static let playerControlsSettingsEnabledDefault = false
|
||||
#endif
|
||||
static let playerControlsSettingsEnabled = Key<Bool>("playerControlsSettingsEnabled", default: playerControlsSettingsEnabledDefault)
|
||||
static let playerControlsCloseEnabled = Key<Bool>("playerControlsCloseEnabled", default: true)
|
||||
static let playerControlsRestartEnabled = Key<Bool>("playerControlsRestartEnabled", default: false)
|
||||
static let playerControlsAdvanceToNextEnabled = Key<Bool>("playerControlsAdvanceToNextEnabled", default: false)
|
||||
static let playerControlsPlaybackModeEnabled = Key<Bool>("playerControlsPlaybackModeEnabled", default: false)
|
||||
static let playerControlsMusicModeEnabled = Key<Bool>("playerControlsMusicModeEnabled", default: false)
|
||||
|
||||
// TODO: IMPLEMENT THIS
|
||||
// ** rgdfo;fgks iojsiojf
|
||||
#if os(macOS)
|
||||
static let playerDetailsPageButtonLabelStyleDefault = ButtonLabelStyle.iconAndText
|
||||
#else
|
||||
static let playerDetailsPageButtonLabelStyleDefault = UIDevice.current.userInterfaceIdiom == .phone ? ButtonLabelStyle.iconOnly : .iconAndText
|
||||
#endif
|
||||
static let playerActionsButtonLabelStyle = Key<ButtonLabelStyle>("playerActionsButtonLabelStyle", default: playerDetailsPageButtonLabelStyleDefault)
|
||||
|
||||
static let actionButtonShareEnabled = Key<Bool>("actionButtonShareEnabled", default: true)
|
||||
static let actionButtonAddToPlaylistEnabled = Key<Bool>("actionButtonAddToPlaylistEnabled", default: true)
|
||||
static let actionButtonSubscribeEnabled = Key<Bool>("actionButtonSubscribeEnabled", default: false)
|
||||
static let actionButtonSettingsEnabled = Key<Bool>("actionButtonSettingsEnabled", default: true)
|
||||
static let actionButtonHideEnabled = Key<Bool>("actionButtonHideEnabled", default: false)
|
||||
static let actionButtonCloseEnabled = Key<Bool>("actionButtonCloseEnabled", default: true)
|
||||
static let actionButtonFullScreenEnabled = Key<Bool>("actionButtonFullScreenEnabled", default: false)
|
||||
static let actionButtonPipEnabled = Key<Bool>("actionButtonPipEnabled", default: false)
|
||||
static let actionButtonLockOrientationEnabled = Key<Bool>("actionButtonLockOrientationEnabled", default: false)
|
||||
static let actionButtonRestartEnabled = Key<Bool>("actionButtonRestartEnabled", default: false)
|
||||
static let actionButtonAdvanceToNextItemEnabled = Key<Bool>("actionButtonAdvanceToNextItemEnabled", default: false)
|
||||
static let actionButtonMusicModeEnabled = Key<Bool>("actionButtonMusicModeEnabled", default: true)
|
||||
|
||||
// MARK: GROUP - Quality
|
||||
|
||||
static let hd2160pMPVProfile = QualityProfile(id: "hd2160pMPVProfile", backend: .mpv, resolution: .hd2160p60, formats: QualityProfile.Format.allCases)
|
||||
static let hd1080pMPVProfile = QualityProfile(id: "hd1080pMPVProfile", backend: .mpv, resolution: .hd1080p60, formats: QualityProfile.Format.allCases)
|
||||
@ -109,150 +219,66 @@ extension Defaults.Keys {
|
||||
static let chargingCellularProfileDefault = hd1080pMPVProfile.id
|
||||
static let chargingNonCellularProfileDefault = hd1080pMPVProfile.id
|
||||
#endif
|
||||
static let playerRate = Key<Double>("playerRate", default: 1.0)
|
||||
static let qualityProfiles = Key<[QualityProfile]>("qualityProfiles", default: qualityProfilesDefault)
|
||||
|
||||
static let batteryCellularProfile = Key<QualityProfile.ID>("batteryCellularProfile", default: batteryCellularProfileDefault)
|
||||
static let batteryNonCellularProfile = Key<QualityProfile.ID>("batteryNonCellularProfile", default: batteryNonCellularProfileDefault)
|
||||
static let chargingCellularProfile = Key<QualityProfile.ID>("chargingCellularProfile", default: chargingCellularProfileDefault)
|
||||
static let chargingNonCellularProfile = Key<QualityProfile.ID>("chargingNonCellularProfile", default: chargingNonCellularProfileDefault)
|
||||
static let forceAVPlayerForLiveStreams = Key<Bool>("forceAVPlayerForLiveStreams", default: true)
|
||||
static let playerSidebar = Key<PlayerSidebarSetting>("playerSidebar", default: .defaultValue)
|
||||
static let playerInstanceID = Key<Instance.ID?>("playerInstance")
|
||||
|
||||
#if os(iOS)
|
||||
static let playerControlsLayoutDefault = UIDevice.current.userInterfaceIdiom == .pad ? PlayerControlsLayout.medium : .small
|
||||
static let fullScreenPlayerControlsLayoutDefault = UIDevice.current.userInterfaceIdiom == .pad ? PlayerControlsLayout.medium : .small
|
||||
#elseif os(tvOS)
|
||||
static let playerControlsLayoutDefault = PlayerControlsLayout.tvRegular
|
||||
static let fullScreenPlayerControlsLayoutDefault = PlayerControlsLayout.tvRegular
|
||||
#else
|
||||
static let playerControlsLayoutDefault = PlayerControlsLayout.medium
|
||||
static let fullScreenPlayerControlsLayoutDefault = PlayerControlsLayout.medium
|
||||
#endif
|
||||
static let qualityProfiles = Key<[QualityProfile]>("qualityProfiles", default: qualityProfilesDefault)
|
||||
|
||||
static let playerControlsLayout = Key<PlayerControlsLayout>("playerControlsLayout", default: playerControlsLayoutDefault)
|
||||
static let fullScreenPlayerControlsLayout = Key<PlayerControlsLayout>("fullScreenPlayerControlsLayout", default: fullScreenPlayerControlsLayoutDefault)
|
||||
static let avPlayerUsesSystemControls = Key<Bool>("avPlayerUsesSystemControls", default: true)
|
||||
static let horizontalPlayerGestureEnabled = Key<Bool>("horizontalPlayerGestureEnabled", default: true)
|
||||
static let seekGestureSpeed = Key<Double>("seekGestureSpeed", default: 0.5)
|
||||
static let seekGestureSensitivity = Key<Double>("seekGestureSensitivity", default: 30.0)
|
||||
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
||||
#if !os(tvOS)
|
||||
static let showScrollToTopInComments = Key<Bool>("showScrollToTopInComments", default: true)
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
static let expandVideoDescriptionDefault = Constants.isIPad
|
||||
#else
|
||||
static let expandVideoDescriptionDefault = true
|
||||
#endif
|
||||
static let expandVideoDescription = Key<Bool>("expandVideoDescription", default: expandVideoDescriptionDefault)
|
||||
static let collapsedLinesDescription = Key<Int>("collapsedLinesDescription", default: 5)
|
||||
|
||||
static let showChannelAvatarInChannelsLists = Key<Bool>("showChannelAvatarInChannelsLists", default: true)
|
||||
static let showChannelAvatarInVideosListing = Key<Bool>("showChannelAvatarInVideosListing", default: true)
|
||||
|
||||
#if os(tvOS)
|
||||
static let pauseOnHidingPlayerDefault = true
|
||||
#else
|
||||
static let pauseOnHidingPlayerDefault = false
|
||||
#endif
|
||||
static let pauseOnHidingPlayer = Key<Bool>("pauseOnHidingPlayer", default: pauseOnHidingPlayerDefault)
|
||||
|
||||
#if !os(macOS)
|
||||
static let pauseOnEnteringBackground = Key<Bool>("pauseOnEnteringBackground", default: true)
|
||||
#endif
|
||||
static let closeVideoOnEOF = Key<Bool>("closeVideoOnEOF", default: false)
|
||||
static let closePiPOnNavigation = Key<Bool>("closePiPOnNavigation", default: false)
|
||||
static let closePiPOnOpeningPlayer = Key<Bool>("closePiPOnOpeningPlayer", default: false)
|
||||
#if !os(macOS)
|
||||
static let closePiPAndOpenPlayerOnEnteringForeground = Key<Bool>("closePiPAndOpenPlayerOnEnteringForeground", default: false)
|
||||
#endif
|
||||
static let closePlayerOnOpeningPiP = Key<Bool>("closePlayerOnOpeningPiP", default: false)
|
||||
|
||||
static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: [])
|
||||
|
||||
static let queue = Key<[PlayerQueueItem]>("queue", default: [])
|
||||
static let saveLastPlayed = Key<Bool>("saveLastPlayed", default: false)
|
||||
static let lastPlayed = Key<PlayerQueueItem?>("lastPlayed")
|
||||
static let playbackMode = Key<PlayerModel.PlaybackMode>("playbackMode", default: .queue)
|
||||
// MARK: GROUP - History
|
||||
|
||||
static let saveRecents = Key<Bool>("saveRecents", default: true)
|
||||
static let saveHistory = Key<Bool>("saveHistory", default: true)
|
||||
static let showWatchingProgress = Key<Bool>("showWatchingProgress", default: true)
|
||||
static let saveLastPlayed = Key<Bool>("saveLastPlayed", default: false)
|
||||
|
||||
static let watchedVideoPlayNowBehavior = Key<WatchedVideoPlayNowBehavior>("watchedVideoPlayNowBehavior", default: .continue)
|
||||
static let watchedThreshold = Key<Int>("watchedThreshold", default: 90)
|
||||
static let resetWatchedStatusOnPlaying = Key<Bool>("resetWatchedStatusOnPlaying", default: false)
|
||||
|
||||
static let watchedVideoStyle = Key<WatchedVideoStyle>("watchedVideoStyle", default: .badge)
|
||||
static let watchedVideoBadgeColor = Key<WatchedVideoBadgeColor>("WatchedVideoBadgeColor", default: .red)
|
||||
static let watchedVideoPlayNowBehavior = Key<WatchedVideoPlayNowBehavior>("watchedVideoPlayNowBehavior", default: .continue)
|
||||
static let resetWatchedStatusOnPlaying = Key<Bool>("resetWatchedStatusOnPlaying", default: false)
|
||||
static let saveRecents = Key<Bool>("saveRecents", default: true)
|
||||
static let showToggleWatchedStatusButton = Key<Bool>("showToggleWatchedStatusButton", default: false)
|
||||
|
||||
static let trendingCategory = Key<TrendingCategory>("trendingCategory", default: .default)
|
||||
static let trendingCountry = Key<Country>("trendingCountry", default: .us)
|
||||
// MARK: GROUP - SponsorBlock
|
||||
|
||||
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.subscriptions, .trending, .playlists])
|
||||
static let startupSection = Key<StartupSection>("startupSection", default: .home)
|
||||
static let sponsorBlockInstance = Key<String>("sponsorBlockInstance", default: "https://sponsor.ajay.app")
|
||||
static let sponsorBlockCategories = Key<Set<String>>("sponsorBlockCategories", default: Set(SponsorBlockAPI.categories))
|
||||
|
||||
#if os(iOS)
|
||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||
static let rotateToLandscapeOnEnterFullScreen = Key<FullScreenRotationSetting>(
|
||||
"rotateToLandscapeOnEnterFullScreen",
|
||||
default: UIDevice.current.userInterfaceIdiom == .phone ? .landscapeRight : .disabled
|
||||
)
|
||||
#endif
|
||||
// MARK: GROUP - Locations
|
||||
|
||||
static let instancesManifest = Key<String>("instancesManifest", default: "")
|
||||
static let countryOfPublicInstances = Key<String?>("countryOfPublicInstances")
|
||||
|
||||
static let instances = Key<[Instance]>("instances", default: [])
|
||||
static let accounts = Key<[Account]>("accounts", default: [])
|
||||
|
||||
// MARK: Group - Advanced
|
||||
|
||||
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||
static let showPlayNowInBackendContextMenu = Key<Bool>("showPlayNowInBackendContextMenu", default: false)
|
||||
|
||||
#if os(macOS)
|
||||
static let playerDetailsPageButtonLabelStyleDefault = ButtonLabelStyle.iconAndText
|
||||
#else
|
||||
static let playerDetailsPageButtonLabelStyleDefault = UIDevice.current.userInterfaceIdiom == .phone ? ButtonLabelStyle.iconOnly : .iconAndText
|
||||
#endif
|
||||
static let playerActionsButtonLabelStyle = Key<ButtonLabelStyle>("playerActionsButtonLabelStyle", default: .iconAndText)
|
||||
|
||||
static let systemControlsCommands = Key<SystemControlsCommands>("systemControlsCommands", default: .restartAndAdvanceToNext)
|
||||
|
||||
static let buttonBackwardSeekDuration = Key<String>("buttonBackwardSeekDuration", default: "10")
|
||||
static let buttonForwardSeekDuration = Key<String>("buttonForwardSeekDuration", default: "10")
|
||||
static let gestureBackwardSeekDuration = Key<String>("gestureBackwardSeekDuration", default: "10")
|
||||
static let gestureForwardSeekDuration = Key<String>("gestureForwardSeekDuration", default: "10")
|
||||
static let systemControlsSeekDuration = Key<String>("systemControlsBackwardSeekDuration", default: "10")
|
||||
static let actionButtonShareEnabled = Key<Bool>("actionButtonShareEnabled", default: true)
|
||||
static let actionButtonAddToPlaylistEnabled = Key<Bool>("actionButtonAddToPlaylistEnabled", default: true)
|
||||
static let actionButtonSubscribeEnabled = Key<Bool>("actionButtonSubscribeEnabled", default: false)
|
||||
static let actionButtonSettingsEnabled = Key<Bool>("actionButtonSettingsEnabled", default: true)
|
||||
static let actionButtonHideEnabled = Key<Bool>("actionButtonHideEnabled", default: false)
|
||||
static let actionButtonCloseEnabled = Key<Bool>("actionButtonCloseEnabled", default: true)
|
||||
static let actionButtonFullScreenEnabled = Key<Bool>("actionButtonFullScreenEnabled", default: false)
|
||||
static let actionButtonPipEnabled = Key<Bool>("actionButtonPipEnabled", default: false)
|
||||
static let actionButtonLockOrientationEnabled = Key<Bool>("actionButtonLockOrientationEnabled", default: false)
|
||||
static let actionButtonRestartEnabled = Key<Bool>("actionButtonRestartEnabled", default: false)
|
||||
static let actionButtonAdvanceToNextItemEnabled = Key<Bool>("actionButtonAdvanceToNextItemEnabled", default: false)
|
||||
static let actionButtonMusicModeEnabled = Key<Bool>("actionButtonMusicModeEnabled", default: true)
|
||||
|
||||
#if os(iOS)
|
||||
static let playerControlsLockOrientationEnabled = Key<Bool>("playerControlsLockOrientationEnabled", default: true)
|
||||
#endif
|
||||
#if os(tvOS)
|
||||
static let playerControlsSettingsEnabledDefault = true
|
||||
#else
|
||||
static let playerControlsSettingsEnabledDefault = false
|
||||
#endif
|
||||
static let playerControlsSettingsEnabled = Key<Bool>("playerControlsSettingsEnabled", default: playerControlsSettingsEnabledDefault)
|
||||
static let playerControlsCloseEnabled = Key<Bool>("playerControlsCloseEnabled", default: true)
|
||||
static let playerControlsRestartEnabled = Key<Bool>("playerControlsRestartEnabled", default: false)
|
||||
static let playerControlsAdvanceToNextEnabled = Key<Bool>("playerControlsAdvanceToNextEnabled", default: false)
|
||||
static let playerControlsPlaybackModeEnabled = Key<Bool>("playerControlsPlaybackModeEnabled", default: false)
|
||||
static let playerControlsMusicModeEnabled = Key<Bool>("playerControlsMusicModeEnabled", default: false)
|
||||
|
||||
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||
static let mpvEnableLogging = Key<Bool>("mpvEnableLogging", default: false)
|
||||
static let mpvCacheSecs = Key<String>("mpvCacheSecs", default: "120")
|
||||
static let mpvCachePauseWait = Key<String>("mpvCachePauseWait", default: "3")
|
||||
static let mpvEnableLogging = Key<Bool>("mpvEnableLogging", default: false)
|
||||
|
||||
static let showCacheStatus = Key<Bool>("showCacheStatus", default: false)
|
||||
static let feedCacheSize = Key<String>("feedCacheSize", default: "50")
|
||||
|
||||
// MARK: GROUP - Other exportable
|
||||
|
||||
static let lastAccountID = Key<Account.ID?>("lastAccountID")
|
||||
static let lastInstanceID = Key<Instance.ID?>("lastInstanceID")
|
||||
|
||||
static let playerRate = Key<Double>("playerRate", default: 1.0)
|
||||
static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: [])
|
||||
|
||||
static let trendingCategory = Key<TrendingCategory>("trendingCategory", default: .default)
|
||||
static let trendingCountry = Key<Country>("trendingCountry", default: .us)
|
||||
|
||||
static let subscriptionsViewPage = Key<SubscriptionsView.Page>("subscriptionsViewPage", default: .feed)
|
||||
|
||||
static let subscriptionsListingStyle = Key<ListingStyle>("subscriptionsListingStyle", default: .cells)
|
||||
@ -263,11 +289,22 @@ extension Defaults.Keys {
|
||||
static let searchListingStyle = Key<ListingStyle>("searchListingStyle", default: .cells)
|
||||
static let hideShorts = Key<Bool>("hideShorts", default: false)
|
||||
static let hideWatched = Key<Bool>("hideWatched", default: false)
|
||||
static let showInspector = Key<ShowInspectorSetting>("showInspector", default: .onlyLocal)
|
||||
static let showChapters = Key<Bool>("showChapters", default: true)
|
||||
static let expandChapters = Key<Bool>("expandChapters", default: true)
|
||||
static let showRelated = Key<Bool>("showRelated", default: true)
|
||||
static let widgetsSettings = Key<[WidgetSettings]>("widgetsSettings", default: [])
|
||||
|
||||
// MARK: GROUP - Not exportable
|
||||
|
||||
static let queue = Key<[PlayerQueueItem]>("queue", default: [])
|
||||
static let playbackMode = Key<PlayerModel.PlaybackMode>("playbackMode", default: .queue)
|
||||
static let lastPlayed = Key<PlayerQueueItem?>("lastPlayed")
|
||||
|
||||
static let activeBackend = Key<PlayerBackendType>("activeBackend", default: .mpv)
|
||||
static let captionsLanguageCode = Key<String?>("captionsLanguageCode")
|
||||
|
||||
static let lastUsedPlaylistID = Key<Playlist.ID?>("lastPlaylistID")
|
||||
static let lastAccountIsPublic = Key<Bool>("lastAccountIsPublic", default: false)
|
||||
|
||||
// MARK: LEGACY
|
||||
|
||||
static let homeHistoryItems = Key<Int>("homeHistoryItems", default: 10)
|
||||
}
|
||||
|
||||
enum ResolutionSetting: String, CaseIterable, Defaults.Serializable {
|
||||
|
@ -22,7 +22,6 @@ struct HomeView: View {
|
||||
@Default(.favorites) private var favorites
|
||||
@Default(.widgetsSettings) private var widgetsSettings
|
||||
#endif
|
||||
@Default(.homeHistoryItems) private var homeHistoryItems
|
||||
@Default(.showFavoritesInHome) private var showFavoritesInHome
|
||||
@Default(.showOpenActionsInHome) private var showOpenActionsInHome
|
||||
@Default(.showQueueInHome) private var showQueueInHome
|
||||
|
@ -68,6 +68,7 @@ struct ContentView: View {
|
||||
SettingsView()
|
||||
}
|
||||
)
|
||||
.modifier(ImportSettingsSheetViewModifier(isPresented: $navigation.presentingSettingsImportSheet, settingsFile: $navigation.settingsImportURL))
|
||||
.background(
|
||||
EmptyView().sheet(isPresented: $navigation.presentingAccounts) {
|
||||
AccountsView()
|
||||
|
@ -14,6 +14,11 @@ struct OpenURLHandler {
|
||||
var navigationStyle: NavigationStyle
|
||||
|
||||
func handle(_ url: URL) {
|
||||
if url.isFileURL, url.standardizedFileURL.absoluteString.hasSuffix(".\(ImportExportSettingsModel.settingsExtension)") {
|
||||
navigation.presentSettingsImportSheet(url)
|
||||
return
|
||||
}
|
||||
|
||||
if Self.firstHandle {
|
||||
Self.firstHandle = false
|
||||
|
||||
|
@ -153,7 +153,7 @@ struct AccountForm: View {
|
||||
return
|
||||
}
|
||||
|
||||
let account = AccountsModel.add(instance: instance, name: name, username: username, password: password)
|
||||
let account = AccountsModel.add(instance: instance, id: nil, name: name, username: username, password: password)
|
||||
selectedAccount?.wrappedValue = account
|
||||
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
|
165
Shared/Settings/ExportSettings.swift
Normal file
165
Shared/Settings/ExportSettings.swift
Normal file
@ -0,0 +1,165 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ExportSettings: View {
|
||||
@ObservedObject private var model = ImportExportSettingsModel.shared
|
||||
@State private var presentingShareSheet = false
|
||||
@StateObject private var settings = SettingsModel.shared
|
||||
|
||||
private var filesToShare = [ImportExportSettingsModel.exportFile]
|
||||
@ObservedObject private var navigation = NavigationModel.shared
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(macOS)
|
||||
VStack {
|
||||
list
|
||||
|
||||
importExportButtons
|
||||
}
|
||||
#else
|
||||
list
|
||||
#if os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
.sheet(
|
||||
isPresented: $presentingShareSheet,
|
||||
onDismiss: { self.model.isExportInProgress = false }
|
||||
) {
|
||||
ShareSheet(activityItems: filesToShare)
|
||||
.id("settings-share-\(filesToShare.count)")
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
.navigationTitle("Export Settings")
|
||||
}
|
||||
|
||||
var list: some View {
|
||||
List {
|
||||
exportView
|
||||
}
|
||||
.onAppear {
|
||||
model.reset()
|
||||
}
|
||||
}
|
||||
|
||||
var importExportButtons: some View {
|
||||
HStack {
|
||||
importButton
|
||||
|
||||
Spacer()
|
||||
|
||||
exportButton
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var importButton: some View {
|
||||
#if os(macOS)
|
||||
Button {
|
||||
navigation.presentingSettingsFileImporter = true
|
||||
} label: {
|
||||
Label("Import", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ExportGroupRow: View {
|
||||
let group: ImportExportSettingsModel.ExportGroup
|
||||
|
||||
@ObservedObject private var model = ImportExportSettingsModel.shared
|
||||
|
||||
var body: some View {
|
||||
Button(action: { model.toggleExportGroupSelection(group) }) {
|
||||
HStack {
|
||||
Text(group.label)
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accent)
|
||||
.opacity(isGroupInSelectedGroups ? 1 : 0)
|
||||
}
|
||||
.animation(nil, value: isGroupInSelectedGroups)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
}
|
||||
|
||||
var isGroupInSelectedGroups: Bool {
|
||||
model.selectedExportGroups.contains(group)
|
||||
}
|
||||
}
|
||||
|
||||
var exportView: some View {
|
||||
Group {
|
||||
Section(header: Text("Settings")) {
|
||||
ForEach(ImportExportSettingsModel.ExportGroup.settingsGroups) { group in
|
||||
ExportGroupRow(group: group)
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Locations")) {
|
||||
ForEach(ImportExportSettingsModel.ExportGroup.locationsGroups) { group in
|
||||
ExportGroupRow(group: group)
|
||||
.disabled(!model.isGroupEnabled(group))
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Other"), footer: otherGroupsFooter) {
|
||||
ForEach(ImportExportSettingsModel.ExportGroup.otherGroups) { group in
|
||||
ExportGroupRow(group: group)
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(macOS)
|
||||
Section {
|
||||
exportButton
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(model.isExportInProgress)
|
||||
}
|
||||
|
||||
var exportButton: some View {
|
||||
Button(action: exportSettings) {
|
||||
Label(model.isExportInProgress ? "Export in progress..." : "Export...", systemImage: model.isExportInProgress ? "fireworks" : "square.and.arrow.up")
|
||||
.animation(nil, value: model.isExportInProgress)
|
||||
#if !os(macOS)
|
||||
.foregroundColor(.accent)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
#endif
|
||||
}
|
||||
.disabled(!model.isExportAvailable)
|
||||
}
|
||||
|
||||
@ViewBuilder var otherGroupsFooter: some View {
|
||||
Text("Other data include last used playback preferences and listing options")
|
||||
}
|
||||
|
||||
func exportSettings() {
|
||||
let export = {
|
||||
model.isExportInProgress = true
|
||||
Delay.by(0.3) {
|
||||
model.exportAction()
|
||||
#if !os(macOS)
|
||||
self.presentingShareSheet = true
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if model.isGroupSelected(.accountsUnencryptedPasswords) {
|
||||
settings.presentAlert(Alert(
|
||||
title: Text("Are you sure you want to export unencrypted passwords?"),
|
||||
message: Text("Do not share this file with anyone or you can lose access to your accounts. If you don't select to export passwords you will be asked to provide them during import"),
|
||||
primaryButton: .destructive(Text("Export"), action: export),
|
||||
secondaryButton: .cancel()
|
||||
))
|
||||
} else {
|
||||
export()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationView {
|
||||
ExportSettings()
|
||||
}
|
||||
}
|
187
Shared/Settings/Import/ImportSettingsAccountRow.swift
Normal file
187
Shared/Settings/Import/ImportSettingsAccountRow.swift
Normal file
@ -0,0 +1,187 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ImportSettingsAccountRow: View {
|
||||
var account: Account
|
||||
var fileModel: ImportSettingsFileModel
|
||||
|
||||
@State private var password = ""
|
||||
|
||||
@State private var isValid = false
|
||||
@State private var isValidated = false
|
||||
@State private var isValidating = false
|
||||
@State private var validationError: String?
|
||||
@State private var validationDebounce = Debounce()
|
||||
|
||||
@ObservedObject private var model = ImportSettingsSheetViewModel.shared
|
||||
|
||||
func afterValidation() {
|
||||
if isValid {
|
||||
model.importableAccounts.insert(account.id)
|
||||
model.selectedAccounts.insert(account.id)
|
||||
model.importableAccountsPasswords[account.id] = password
|
||||
} else {
|
||||
model.selectedAccounts.remove(account.id)
|
||||
model.importableAccounts.remove(account.id)
|
||||
model.importableAccountsPasswords.removeValue(forKey: account.id)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: { model.toggleAccount(account, accounts: accounts) }) {
|
||||
let accountExists = AccountsModel.shared.find(account.id) != nil
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text(account.username)
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accentColor)
|
||||
.opacity(isChecked ? 1 : 0)
|
||||
}
|
||||
Text(account.instance?.description ?? "")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Group {
|
||||
if let instanceID = account.instanceID {
|
||||
if accountExists {
|
||||
HStack {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(Color("AppRedColor"))
|
||||
Text("Account already exists")
|
||||
}
|
||||
} else {
|
||||
Group {
|
||||
if InstancesModel.shared.find(instanceID) != nil {
|
||||
HStack {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("Custom Location already exists")
|
||||
}
|
||||
} else if model.selectedInstances.contains(instanceID) {
|
||||
HStack {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("Custom Location selected for import")
|
||||
}
|
||||
} else {
|
||||
HStack {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
Text("Custom Location not selected for import")
|
||||
}
|
||||
.foregroundColor(Color("AppRedColor"))
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 20)
|
||||
|
||||
if account.password.isNil || account.password!.isEmpty {
|
||||
Group {
|
||||
if password.isEmpty {
|
||||
HStack {
|
||||
Image(systemName: "key")
|
||||
Text("Password required to import")
|
||||
}
|
||||
.foregroundColor(Color("AppRedColor"))
|
||||
} else {
|
||||
AccountValidationStatus(
|
||||
app: .constant(instance.app),
|
||||
isValid: $isValid,
|
||||
isValidated: $isValidated,
|
||||
isValidating: $isValidating,
|
||||
error: $validationError
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 20)
|
||||
} else {
|
||||
HStack {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
|
||||
Text("Password saved in import file")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
.font(.caption)
|
||||
.padding(.vertical, 2)
|
||||
|
||||
if !accountExists && (account.password.isNil || account.password!.isEmpty) {
|
||||
SecureField("Password", text: $password)
|
||||
.onChange(of: password) { _ in validate() }
|
||||
#if !os(tvOS)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
.onChange(of: isValid) { _ in afterValidation() }
|
||||
.animation(nil, value: isChecked)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
var isChecked: Bool {
|
||||
model.isSelectedForImport(account)
|
||||
}
|
||||
|
||||
var locationsSettingsGroupImporter: LocationsSettingsGroupImporter? {
|
||||
fileModel.locationsSettingsGroupImporter
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
fileModel.locationsSettingsGroupImporter?.accounts ?? []
|
||||
}
|
||||
|
||||
private var instance: Instance! {
|
||||
(fileModel.locationsSettingsGroupImporter?.instances ?? []).first { $0.id == account.instanceID }
|
||||
}
|
||||
|
||||
private var validator: AccountValidator {
|
||||
AccountValidator(
|
||||
app: .constant(instance.app),
|
||||
url: instance.apiURLString,
|
||||
account: Account(instanceID: instance.id, urlString: instance.apiURLString, username: account.username, password: password),
|
||||
id: .constant(account.username),
|
||||
isValid: $isValid,
|
||||
isValidated: $isValidated,
|
||||
isValidating: $isValidating,
|
||||
error: $validationError
|
||||
)
|
||||
}
|
||||
|
||||
private func validate() {
|
||||
isValid = false
|
||||
validationDebounce.invalidate()
|
||||
|
||||
guard !account.username.isEmpty, !password.isEmpty else {
|
||||
validator.reset()
|
||||
return
|
||||
}
|
||||
|
||||
isValidating = true
|
||||
|
||||
validationDebounce.debouncing(1) {
|
||||
validator.validateAccount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
let fileModel = ImportSettingsFileModel(url: URL(string: "https://gist.githubusercontent.com/arekf/578668969c9fdef1b3828bea864c3956/raw/f794a95a20261bcb1145e656c8dda00bea339e2a/yattee-recents.yatteesettings")!)
|
||||
|
||||
return List {
|
||||
ImportSettingsAccountRow(
|
||||
account: .init(name: "arekf", urlString: "https://instance.com", username: "arekf"),
|
||||
fileModel: fileModel
|
||||
)
|
||||
ImportSettingsAccountRow(
|
||||
account: .init(name: "arekf", urlString: "https://instance.com", username: "arekf", password: "a"),
|
||||
fileModel: fileModel
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct ImportSettingsFileImporterViewModifier: ViewModifier {
|
||||
@Binding var isPresented: Bool
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.fileImporter(isPresented: $isPresented, allowedContentTypes: [.json]) { result in
|
||||
do {
|
||||
let selectedFile = try result.get()
|
||||
var urlToOpen: URL?
|
||||
|
||||
if let bookmarkURL = URLBookmarkModel.shared.loadBookmark(selectedFile) {
|
||||
urlToOpen = bookmarkURL
|
||||
}
|
||||
|
||||
if selectedFile.startAccessingSecurityScopedResource() {
|
||||
URLBookmarkModel.shared.saveBookmark(selectedFile)
|
||||
urlToOpen = selectedFile
|
||||
}
|
||||
|
||||
guard let urlToOpen else { return }
|
||||
NavigationModel.shared.presentSettingsImportSheet(urlToOpen, forceSettings: true)
|
||||
} catch {
|
||||
NavigationModel.shared.presentAlert(title: "Could not open Files")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
260
Shared/Settings/Import/ImportSettingsSheetView.swift
Normal file
260
Shared/Settings/Import/ImportSettingsSheetView.swift
Normal file
@ -0,0 +1,260 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ImportSettingsSheetView: View {
|
||||
@Binding var settingsFile: URL?
|
||||
@StateObject private var model = ImportSettingsSheetViewModel.shared
|
||||
@StateObject private var importExportModel = ImportExportSettingsModel.shared
|
||||
|
||||
@Environment(\.presentationMode) private var presentationMode
|
||||
|
||||
@State private var presentingCompletedAlert = false
|
||||
|
||||
private let accountsModel = AccountsModel.shared
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(macOS)
|
||||
list
|
||||
.frame(width: 700, height: 800)
|
||||
#else
|
||||
NavigationView {
|
||||
list
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.onAppear {
|
||||
guard let fileModel else { return }
|
||||
model.reset(fileModel.locationsSettingsGroupImporter)
|
||||
importExportModel.reset(fileModel)
|
||||
}
|
||||
.onChange(of: settingsFile) { _ in
|
||||
importExportModel.reset(fileModel)
|
||||
}
|
||||
}
|
||||
|
||||
var list: some View {
|
||||
List {
|
||||
importGroupView
|
||||
|
||||
importOptions
|
||||
|
||||
metadata
|
||||
}
|
||||
.alert(isPresented: $presentingCompletedAlert) {
|
||||
completedAlert
|
||||
}
|
||||
#if os(iOS)
|
||||
.backport
|
||||
.scrollDismissesKeyboardInteractively()
|
||||
#endif
|
||||
.navigationTitle("Import Settings")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(action: { presentationMode.wrappedValue.dismiss() }) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(action: {
|
||||
fileModel?.performImport()
|
||||
presentingCompletedAlert = true
|
||||
ImportExportSettingsModel.shared.reset()
|
||||
}) {
|
||||
Text("Import")
|
||||
}
|
||||
.disabled(!canImport)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var completedAlert: Alert {
|
||||
Alert(
|
||||
title: Text("Import Completed"),
|
||||
dismissButton: .default(Text("Close")) {
|
||||
if accountsModel.isEmpty,
|
||||
let account = InstancesModel.shared.all.first?.anonymousAccount
|
||||
{
|
||||
accountsModel.setCurrent(account)
|
||||
}
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var canImport: Bool {
|
||||
return !model.selectedAccounts.isEmpty || !model.selectedInstances.isEmpty || !importExportModel.selectedExportGroups.isEmpty
|
||||
}
|
||||
|
||||
var fileModel: ImportSettingsFileModel? {
|
||||
guard let settingsFile else { return nil }
|
||||
|
||||
return ImportSettingsFileModel(url: settingsFile)
|
||||
}
|
||||
|
||||
var locationsSettingsGroupImporter: LocationsSettingsGroupImporter? {
|
||||
guard let fileModel else { return nil }
|
||||
|
||||
return fileModel.locationsSettingsGroupImporter
|
||||
}
|
||||
|
||||
struct ExportGroupRow: View {
|
||||
let group: ImportExportSettingsModel.ExportGroup
|
||||
|
||||
@ObservedObject private var model = ImportExportSettingsModel.shared
|
||||
|
||||
var body: some View {
|
||||
Button(action: { model.toggleExportGroupSelection(group) }) {
|
||||
HStack {
|
||||
Text(group.label)
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accent)
|
||||
.opacity(isChecked ? 1 : 0)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.foregroundColor(.primary)
|
||||
.animation(nil, value: isChecked)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
var isChecked: Bool {
|
||||
model.selectedExportGroups.contains(group)
|
||||
}
|
||||
}
|
||||
|
||||
var importGroupView: some View {
|
||||
Group {
|
||||
Section(header: Text("Settings")) {
|
||||
ForEach(ImportExportSettingsModel.ExportGroup.settingsGroups) { group in
|
||||
ExportGroupRow(group: group)
|
||||
.disabled(!fileModel!.isGroupIncludedInFile(group))
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Other")) {
|
||||
ForEach(ImportExportSettingsModel.ExportGroup.otherGroups) { group in
|
||||
ExportGroupRow(group: group)
|
||||
.disabled(!fileModel!.isGroupIncludedInFile(group))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var metadata: some View {
|
||||
if let fileModel {
|
||||
Section(header: Text("File information")) {
|
||||
MetadataRow(name: Text("Name"), value: Text(fileModel.filename))
|
||||
|
||||
if let date = fileModel.metadataDate {
|
||||
MetadataRow(name: Text("Date"), value: Text(date))
|
||||
}
|
||||
|
||||
if let build = fileModel.metadataBuild {
|
||||
MetadataRow(name: Text("Build"), value: Text(build))
|
||||
}
|
||||
|
||||
if let platform = fileModel.metadataPlatform {
|
||||
MetadataRow(name: Text("Platform"), value: Text(platform))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MetadataRow: View {
|
||||
let name: Text
|
||||
let value: Text
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
name
|
||||
.layoutPriority(2)
|
||||
|
||||
Spacer()
|
||||
|
||||
value
|
||||
.layoutPriority(1)
|
||||
.lineLimit(2)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var instances: [Instance] {
|
||||
locationsSettingsGroupImporter?.instances ?? []
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
locationsSettingsGroupImporter?.accounts ?? []
|
||||
}
|
||||
|
||||
struct ImportInstanceRow: View {
|
||||
var instance: Instance
|
||||
var accounts: [Account]
|
||||
|
||||
@ObservedObject private var model = ImportSettingsSheetViewModel.shared
|
||||
|
||||
var body: some View {
|
||||
Button(action: { model.toggleInstance(instance, accounts: accounts) }) {
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text(instance.description)
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
.opacity(isChecked ? 1 : 0)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
|
||||
if model.isInstanceAlreadyAdded(instance) {
|
||||
HStack {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.foregroundColor(.red)
|
||||
Text("Custom Location already exists")
|
||||
}
|
||||
.font(.caption)
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.foregroundColor(.primary)
|
||||
.transaction { t in t.animation = nil }
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
var isChecked: Bool {
|
||||
model.isImportable(instance) && model.selectedInstances.contains(instance.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder var importOptions: some View {
|
||||
if let fileModel {
|
||||
if fileModel.isPublicInstancesSettingsGroupInFile || !instances.isEmpty {
|
||||
Section(header: Text("Locations")) {
|
||||
if fileModel.isPublicInstancesSettingsGroupInFile {
|
||||
ExportGroupRow(group: .locationsSettings)
|
||||
}
|
||||
|
||||
ForEach(instances) { instance in
|
||||
ImportInstanceRow(instance: instance, accounts: accounts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !accounts.isEmpty {
|
||||
Section(header: Text("Accounts")) {
|
||||
ForEach(accounts) { account in
|
||||
ImportSettingsAccountRow(account: account, fileModel: fileModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ImportSettingsSheetView(settingsFile: .constant(URL(string: "https://gist.githubusercontent.com/arekf/578668969c9fdef1b3828bea864c3956/raw/f794a95a20261bcb1145e656c8dda00bea339e2a/yattee-recents.yatteesettings")!))
|
||||
}
|
77
Shared/Settings/Import/ImportSettingsSheetViewModel.swift
Normal file
77
Shared/Settings/Import/ImportSettingsSheetViewModel.swift
Normal file
@ -0,0 +1,77 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
class ImportSettingsSheetViewModel: ObservableObject {
|
||||
static let shared = ImportSettingsSheetViewModel()
|
||||
|
||||
@Published var selectedInstances = Set<Instance.ID>()
|
||||
@Published var selectedAccounts = Set<Account.ID>()
|
||||
|
||||
@Published var importableAccounts = Set<Account.ID>()
|
||||
@Published var importableAccountsPasswords = [Account.ID: String]()
|
||||
|
||||
func toggleInstance(_ instance: Instance, accounts: [Account]) {
|
||||
if selectedInstances.contains(instance.id) {
|
||||
selectedInstances.remove(instance.id)
|
||||
} else {
|
||||
guard isImportable(instance) else { return }
|
||||
selectedInstances.insert(instance.id)
|
||||
}
|
||||
|
||||
removeNonImportableFromSelectedAccounts(accounts: accounts)
|
||||
}
|
||||
|
||||
func toggleAccount(_ account: Account, accounts: [Account]) {
|
||||
if selectedAccounts.contains(account.id) {
|
||||
selectedAccounts.remove(account.id)
|
||||
} else {
|
||||
guard isImportable(account.id, accounts: accounts) else { return }
|
||||
selectedAccounts.insert(account.id)
|
||||
}
|
||||
}
|
||||
|
||||
func isSelectedForImport(_ account: Account) -> Bool {
|
||||
importableAccounts.contains(account.id) && selectedAccounts.contains(account.id)
|
||||
}
|
||||
|
||||
func isImportable(_ accountID: Account.ID, accounts: [Account]) -> Bool {
|
||||
guard let account = accounts.first(where: { $0.id == accountID }),
|
||||
let instanceID = account.instanceID,
|
||||
AccountsModel.shared.find(accountID) == nil
|
||||
else { return false }
|
||||
|
||||
return ((account.password != nil && !account.password!.isEmpty) ||
|
||||
importableAccounts.contains(account.id)) && (
|
||||
(InstancesModel.shared.find(instanceID) != nil) ||
|
||||
selectedInstances.contains(instanceID)
|
||||
)
|
||||
}
|
||||
|
||||
func isImportable(_ instance: Instance) -> Bool {
|
||||
!isInstanceAlreadyAdded(instance)
|
||||
}
|
||||
|
||||
func isInstanceAlreadyAdded(_ instance: Instance) -> Bool {
|
||||
InstancesModel.shared.find(instance.id) != nil || InstancesModel.shared.findByURLString(instance.apiURLString) != nil
|
||||
}
|
||||
|
||||
func removeNonImportableFromSelectedAccounts(accounts: [Account]) {
|
||||
selectedAccounts = Set(selectedAccounts.filter { isImportable($0, accounts: accounts) })
|
||||
}
|
||||
|
||||
func reset() {
|
||||
selectedAccounts = []
|
||||
selectedInstances = []
|
||||
importableAccounts = []
|
||||
}
|
||||
|
||||
func reset(_ importer: LocationsSettingsGroupImporter? = nil) {
|
||||
reset()
|
||||
|
||||
guard let importer else { return }
|
||||
|
||||
selectedInstances = Set(importer.instances.filter { isImportable($0) }.map(\.id))
|
||||
importableAccounts = Set(importer.accounts.filter { isImportable($0.id, accounts: importer.accounts) }.map(\.id))
|
||||
selectedAccounts = importableAccounts
|
||||
}
|
||||
}
|
25
Shared/Settings/Import/ImportSettingsSheetViewModifier.swift
Normal file
25
Shared/Settings/Import/ImportSettingsSheetViewModifier.swift
Normal file
@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import SwiftyJSON
|
||||
|
||||
struct ImportSettingsSheetViewModifier: ViewModifier {
|
||||
@Binding var isPresented: Bool
|
||||
@Binding var settingsFile: URL?
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.sheet(isPresented: $isPresented) {
|
||||
ImportSettingsSheetView(settingsFile: $settingsFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
Text("")
|
||||
.modifier(
|
||||
ImportSettingsSheetViewModifier(
|
||||
isPresented: .constant(true),
|
||||
settingsFile: .constant(URL(string: "https://gist.githubusercontent.com/arekf/87b4d6702755b01139431dcb809f9fdc/raw/7bb5cdba3ffc0c479f5260430ddc43c4a79a7a72/yattee-177-iPhone.yatteesettings")!)
|
||||
)
|
||||
)
|
||||
}
|
@ -7,7 +7,7 @@ struct SettingsView: View {
|
||||
|
||||
#if os(macOS)
|
||||
private enum Tabs: Hashable {
|
||||
case browsing, player, controls, quality, history, sponsorBlock, locations, advanced, help
|
||||
case browsing, player, controls, quality, history, sponsorBlock, locations, advanced, importExport, help
|
||||
}
|
||||
|
||||
@State private var selection: Tabs = .browsing
|
||||
@ -24,13 +24,22 @@ struct SettingsView: View {
|
||||
|
||||
@Default(.instances) private var instances
|
||||
|
||||
@State private var filesToShare = []
|
||||
|
||||
@ObservedObject private var navigation = NavigationModel.shared
|
||||
@ObservedObject private var settingsModel = SettingsModel.shared
|
||||
|
||||
var body: some View {
|
||||
settings
|
||||
.alert(isPresented: $model.presentingAlert) { model.alert }
|
||||
#if os(iOS)
|
||||
.backport
|
||||
.scrollDismissesKeyboardInteractively()
|
||||
#if !os(tvOS)
|
||||
.modifier(ImportSettingsFileImporterViewModifier(isPresented: $navigation.presentingSettingsFileImporter))
|
||||
.modifier(ImportSettingsSheetViewModifier(isPresented: $settingsModel.presentingSettingsImportSheet, settingsFile: $settingsModel.settingsImportURL))
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.backport
|
||||
.scrollDismissesKeyboardInteractively()
|
||||
#endif
|
||||
.alert(isPresented: $model.presentingAlert) { model.alert }
|
||||
}
|
||||
|
||||
var settings: some View {
|
||||
@ -101,6 +110,14 @@ struct SettingsView: View {
|
||||
}
|
||||
.tag(Tabs.advanced)
|
||||
|
||||
Group {
|
||||
ExportSettings()
|
||||
}
|
||||
.tabItem {
|
||||
Label("Export", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
.tag(Tabs.importExport)
|
||||
|
||||
Form {
|
||||
Help()
|
||||
}
|
||||
@ -110,7 +127,7 @@ struct SettingsView: View {
|
||||
.tag(Tabs.help)
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 650, height: windowHeight)
|
||||
.frame(width: 700, height: windowHeight)
|
||||
#else
|
||||
NavigationView {
|
||||
settingsList
|
||||
@ -206,6 +223,8 @@ struct SettingsView: View {
|
||||
.padding(.horizontal, 20)
|
||||
#endif
|
||||
|
||||
importView
|
||||
|
||||
Section(footer: helpFooter) {
|
||||
NavigationLink {
|
||||
Help()
|
||||
@ -260,6 +279,28 @@ struct SettingsView: View {
|
||||
}
|
||||
#endif
|
||||
|
||||
var importView: some View {
|
||||
Section {
|
||||
Button(action: importSettings) {
|
||||
Label("Import Settings...", systemImage: "square.and.arrow.down")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.foregroundColor(.accent)
|
||||
.buttonStyle(.plain)
|
||||
|
||||
NavigationLink(destination: LazyView(ExportSettings())) {
|
||||
Label("Export Settings", systemImage: "square.and.arrow.up")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importSettings() {
|
||||
navigation.presentingSettingsFileImporter = true
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private var windowHeight: Double {
|
||||
switch selection {
|
||||
@ -278,7 +319,9 @@ struct SettingsView: View {
|
||||
case .locations:
|
||||
return 600
|
||||
case .advanced:
|
||||
return 380
|
||||
return 500
|
||||
case .importExport:
|
||||
return 580
|
||||
case .help:
|
||||
return 650
|
||||
}
|
||||
|
@ -21,6 +21,14 @@ struct YatteeApp: App {
|
||||
}
|
||||
|
||||
static var logsDirectory: URL {
|
||||
temporaryDirectory
|
||||
}
|
||||
|
||||
static var settingsExportDirectory: URL {
|
||||
temporaryDirectory
|
||||
}
|
||||
|
||||
private static var temporaryDirectory: URL {
|
||||
URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,13 @@
|
||||
372AA410286D067B0000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA40F286D067B0000B1DC /* Repeat */; };
|
||||
372AA412286D06950000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA411286D06950000B1DC /* Repeat */; };
|
||||
372AA414286D06A10000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA413286D06A10000B1DC /* Repeat */; };
|
||||
372C74632B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74622B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift */; };
|
||||
372C74642B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74622B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift */; };
|
||||
372C74662B67044300BE179B /* ImportSettingsSheetViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74652B67044300BE179B /* ImportSettingsSheetViewModifier.swift */; };
|
||||
372C74672B67044300BE179B /* ImportSettingsSheetViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74652B67044300BE179B /* ImportSettingsSheetViewModifier.swift */; };
|
||||
372C74682B67044900BE179B /* ImportSettingsSheetViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74652B67044300BE179B /* ImportSettingsSheetViewModifier.swift */; };
|
||||
372C746A2B67098A00BE179B /* ImportSettingsFileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74692B67098A00BE179B /* ImportSettingsFileModel.swift */; };
|
||||
372C746B2B67098A00BE179B /* ImportSettingsFileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74692B67098A00BE179B /* ImportSettingsFileModel.swift */; };
|
||||
372CFD15285F2E2A00B0B54B /* ControlsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372CFD14285F2E2A00B0B54B /* ControlsBar.swift */; };
|
||||
372CFD16285F2E2A00B0B54B /* ControlsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372CFD14285F2E2A00B0B54B /* ControlsBar.swift */; };
|
||||
372D85DE283841B800FF3C7D /* PiPDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373031F428383A89000CFD59 /* PiPDelegate.swift */; };
|
||||
@ -332,6 +339,12 @@
|
||||
37599F36272B44000087F250 /* FavoritesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37599F33272B44000087F250 /* FavoritesModel.swift */; };
|
||||
37599F38272B4D740087F250 /* FavoriteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37599F37272B4D740087F250 /* FavoriteButton.swift */; };
|
||||
37599F39272B4D740087F250 /* FavoriteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37599F37272B4D740087F250 /* FavoriteButton.swift */; };
|
||||
375AC29A2B66B7D600B680E7 /* ExportSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC2992B66B7D600B680E7 /* ExportSettings.swift */; };
|
||||
375AC29B2B66B7D600B680E7 /* ExportSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC2992B66B7D600B680E7 /* ExportSettings.swift */; };
|
||||
375AC29C2B66B7D600B680E7 /* ExportSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC2992B66B7D600B680E7 /* ExportSettings.swift */; };
|
||||
375AC29E2B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC29D2B66BDD600B680E7 /* ImportExportSettingsModel.swift */; };
|
||||
375AC29F2B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC29D2B66BDD600B680E7 /* ImportExportSettingsModel.swift */; };
|
||||
375AC2A02B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375AC29D2B66BDD600B680E7 /* ImportExportSettingsModel.swift */; };
|
||||
375B537428DF6CBB004C1D19 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 375B537828DF6CBB004C1D19 /* Localizable.strings */; };
|
||||
375B537528DF6CBB004C1D19 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 375B537828DF6CBB004C1D19 /* Localizable.strings */; };
|
||||
375B537628DF6CBB004C1D19 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 375B537828DF6CBB004C1D19 /* Localizable.strings */; };
|
||||
@ -649,6 +662,64 @@
|
||||
37A5DBC8285E371400CA4DD1 /* ControlBackgroundModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */; };
|
||||
37A5DBC9285E371400CA4DD1 /* ControlBackgroundModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */; };
|
||||
37A5DBCA285E371400CA4DD1 /* ControlBackgroundModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */; };
|
||||
37A7D6E32B67E303009CB1ED /* ImportSettingsFileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C74692B67098A00BE179B /* ImportSettingsFileModel.swift */; };
|
||||
37A7D6E52B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E42B67E315009CB1ED /* SettingsGroupExporter.swift */; };
|
||||
37A7D6E62B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E42B67E315009CB1ED /* SettingsGroupExporter.swift */; };
|
||||
37A7D6E72B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E42B67E315009CB1ED /* SettingsGroupExporter.swift */; };
|
||||
37A7D6E92B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E82B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift */; };
|
||||
37A7D6EA2B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E82B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift */; };
|
||||
37A7D6EB2B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6E82B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift */; };
|
||||
37A7D6ED2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6EC2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift */; };
|
||||
37A7D6EE2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6EC2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift */; };
|
||||
37A7D6EF2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6EC2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift */; };
|
||||
37A7D6F32B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F22B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift */; };
|
||||
37A7D6F42B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F22B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift */; };
|
||||
37A7D6F52B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F22B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift */; };
|
||||
37A7D6F72B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F62B68071C009CB1ED /* PlayerSettingsGroupImporter.swift */; };
|
||||
37A7D6F82B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F62B68071C009CB1ED /* PlayerSettingsGroupImporter.swift */; };
|
||||
37A7D6F92B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6F62B68071C009CB1ED /* PlayerSettingsGroupImporter.swift */; };
|
||||
37A7D6FB2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FA2B680822009CB1ED /* ControlsSettingsGroupExporter.swift */; };
|
||||
37A7D6FC2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FA2B680822009CB1ED /* ControlsSettingsGroupExporter.swift */; };
|
||||
37A7D6FD2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FA2B680822009CB1ED /* ControlsSettingsGroupExporter.swift */; };
|
||||
37A7D6FF2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FE2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift */; };
|
||||
37A7D7002B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FE2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift */; };
|
||||
37A7D7012B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D6FE2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift */; };
|
||||
37A7D7032B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7022B680A97009CB1ED /* QualitySettingsGroupExporter.swift */; };
|
||||
37A7D7042B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7022B680A97009CB1ED /* QualitySettingsGroupExporter.swift */; };
|
||||
37A7D7052B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7022B680A97009CB1ED /* QualitySettingsGroupExporter.swift */; };
|
||||
37A7D7072B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7062B680A9E009CB1ED /* QualitySettingsGroupImporter.swift */; };
|
||||
37A7D7082B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7062B680A9E009CB1ED /* QualitySettingsGroupImporter.swift */; };
|
||||
37A7D7092B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7062B680A9E009CB1ED /* QualitySettingsGroupImporter.swift */; };
|
||||
37A7D70B2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70A2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift */; };
|
||||
37A7D70C2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70A2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift */; };
|
||||
37A7D70D2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70A2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift */; };
|
||||
37A7D70F2B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70E2B680CED009CB1ED /* HistorySettingsGroupImporter.swift */; };
|
||||
37A7D7102B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70E2B680CED009CB1ED /* HistorySettingsGroupImporter.swift */; };
|
||||
37A7D7112B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D70E2B680CED009CB1ED /* HistorySettingsGroupImporter.swift */; };
|
||||
37A7D7132B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7122B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift */; };
|
||||
37A7D7142B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7122B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift */; };
|
||||
37A7D7152B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7122B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift */; };
|
||||
37A7D7172B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7162B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift */; };
|
||||
37A7D7182B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7162B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift */; };
|
||||
37A7D7192B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7162B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift */; };
|
||||
37A7D71B2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71A2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift */; };
|
||||
37A7D71C2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71A2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift */; };
|
||||
37A7D71D2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71A2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift */; };
|
||||
37A7D71F2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71E2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift */; };
|
||||
37A7D7202B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71E2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift */; };
|
||||
37A7D7212B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D71E2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift */; };
|
||||
37A7D7232B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7222B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift */; };
|
||||
37A7D7242B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7222B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift */; };
|
||||
37A7D7252B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7222B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift */; };
|
||||
37A7D7272B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7262B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift */; };
|
||||
37A7D7282B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7262B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift */; };
|
||||
37A7D7292B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D7262B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift */; };
|
||||
37A7D72B2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72A2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift */; };
|
||||
37A7D72C2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72A2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift */; };
|
||||
37A7D72D2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72A2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift */; };
|
||||
37A7D72F2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72E2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift */; };
|
||||
37A7D7302B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72E2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift */; };
|
||||
37A7D7312B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A7D72E2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift */; };
|
||||
37A81BF9294BD1440081D322 /* WatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A81BF8294BD1440081D322 /* WatchView.swift */; };
|
||||
37A81BFA294BD1440081D322 /* WatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A81BF8294BD1440081D322 /* WatchView.swift */; };
|
||||
37A81BFB294BD1440081D322 /* WatchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A81BF8294BD1440081D322 /* WatchView.swift */; };
|
||||
@ -712,6 +783,15 @@
|
||||
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */; };
|
||||
37BADCA52699FB72009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA42699FB72009BE4FB /* Alamofire */; };
|
||||
37BADCA9269A570B009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA8269A570B009BE4FB /* Alamofire */; };
|
||||
37BBB33A2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3392B6B9053001C4845 /* ImportSettingsSheetViewModel.swift */; };
|
||||
37BBB33B2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3392B6B9053001C4845 /* ImportSettingsSheetViewModel.swift */; };
|
||||
37BBB33C2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3392B6B9053001C4845 /* ImportSettingsSheetViewModel.swift */; };
|
||||
37BBB33F2B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB33E2B6B9D52001C4845 /* ImportSettingsSheetView.swift */; };
|
||||
37BBB3402B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB33E2B6B9D52001C4845 /* ImportSettingsSheetView.swift */; };
|
||||
37BBB3412B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB33E2B6B9D52001C4845 /* ImportSettingsSheetView.swift */; };
|
||||
37BBB3432B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3422B6BB88F001C4845 /* ImportSettingsAccountRow.swift */; };
|
||||
37BBB3442B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3422B6BB88F001C4845 /* ImportSettingsAccountRow.swift */; };
|
||||
37BBB3452B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BBB3422B6BB88F001C4845 /* ImportSettingsAccountRow.swift */; };
|
||||
37BC50A82778A84700510953 /* HistorySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BC50A72778A84700510953 /* HistorySettings.swift */; };
|
||||
37BC50A92778A84700510953 /* HistorySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BC50A72778A84700510953 /* HistorySettings.swift */; };
|
||||
37BC50AA2778A84700510953 /* HistorySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BC50A72778A84700510953 /* HistorySettings.swift */; };
|
||||
@ -872,6 +952,12 @@
|
||||
37E70927271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; };
|
||||
37E70928271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; };
|
||||
37E70929271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */; };
|
||||
37E75CC72B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CC62B6AEAF7003A6237 /* RecentlyOpenedImporter.swift */; };
|
||||
37E75CC82B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CC62B6AEAF7003A6237 /* RecentlyOpenedImporter.swift */; };
|
||||
37E75CC92B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CC62B6AEAF7003A6237 /* RecentlyOpenedImporter.swift */; };
|
||||
37E75CCB2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CCA2B6AEB01003A6237 /* RecentlyOpenedExporter.swift */; };
|
||||
37E75CCC2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CCA2B6AEB01003A6237 /* RecentlyOpenedExporter.swift */; };
|
||||
37E75CCD2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E75CCA2B6AEB01003A6237 /* RecentlyOpenedExporter.swift */; };
|
||||
37E80F3C287B107F00561799 /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; };
|
||||
37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */; };
|
||||
37E80F40287B472300561799 /* ScrollContentBackground+Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E80F3F287B472300561799 /* ScrollContentBackground+Backport.swift */; };
|
||||
@ -1100,6 +1186,9 @@
|
||||
3728203F2945E4A8009A0E2D /* SubscriptionsPageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsPageButton.swift; sourceTree = "<group>"; };
|
||||
3729037D2739E47400EA99F6 /* MenuCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCommands.swift; sourceTree = "<group>"; };
|
||||
372915E52687E3B900F5A35B /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = "<group>"; };
|
||||
372C74622B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsFileImporterViewModifier.swift; sourceTree = "<group>"; };
|
||||
372C74652B67044300BE179B /* ImportSettingsSheetViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsSheetViewModifier.swift; sourceTree = "<group>"; };
|
||||
372C74692B67098A00BE179B /* ImportSettingsFileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsFileModel.swift; sourceTree = "<group>"; };
|
||||
372CFD14285F2E2A00B0B54B /* ControlsBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsBar.swift; sourceTree = "<group>"; };
|
||||
373031F22838388A000CFD59 /* PlayerLayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerLayerView.swift; sourceTree = "<group>"; tabWidth = 5; };
|
||||
373031F428383A89000CFD59 /* PiPDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPDelegate.swift; sourceTree = "<group>"; };
|
||||
@ -1158,6 +1247,8 @@
|
||||
37599F2F272B42810087F250 /* FavoriteItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteItem.swift; sourceTree = "<group>"; };
|
||||
37599F33272B44000087F250 /* FavoritesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesModel.swift; sourceTree = "<group>"; };
|
||||
37599F37272B4D740087F250 /* FavoriteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteButton.swift; sourceTree = "<group>"; };
|
||||
375AC2992B66B7D600B680E7 /* ExportSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportSettings.swift; sourceTree = "<group>"; };
|
||||
375AC29D2B66BDD600B680E7 /* ImportExportSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportExportSettingsModel.swift; sourceTree = "<group>"; };
|
||||
375B537728DF6CBB004C1D19 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
375B537928DF6CC4004C1D19 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
375B8AB228B580D300397B31 /* KeychainModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainModel.swift; sourceTree = "<group>"; };
|
||||
@ -1269,6 +1360,25 @@
|
||||
37A362BD29537AAA00BDF328 /* PlaybackSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackSettings.swift; sourceTree = "<group>"; };
|
||||
37A362C129537FED00BDF328 /* PlaybackSettingsPresentationDetents+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlaybackSettingsPresentationDetents+Backport.swift"; sourceTree = "<group>"; };
|
||||
37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlBackgroundModifier.swift; sourceTree = "<group>"; };
|
||||
37A7D6E42B67E315009CB1ED /* SettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6E82B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsingSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6EC2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsingSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6F22B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6F62B68071C009CB1ED /* PlayerSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6FA2B680822009CB1ED /* ControlsSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D6FE2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7022B680A97009CB1ED /* QualitySettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualitySettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7062B680A9E009CB1ED /* QualitySettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QualitySettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D70A2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D70E2B680CED009CB1ED /* HistorySettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7122B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7162B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D71A2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D71E2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7222B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D7262B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A7D72A2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherDataSettingsGroupExporter.swift; sourceTree = "<group>"; };
|
||||
37A7D72E2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherDataSettingsGroupImporter.swift; sourceTree = "<group>"; };
|
||||
37A81BF8294BD1440081D322 /* WatchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchView.swift; sourceTree = "<group>"; };
|
||||
37A9965926D6F8CA006E3224 /* HorizontalCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalCells.swift; sourceTree = "<group>"; };
|
||||
37A9965D26D6F9B9006E3224 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
@ -1299,6 +1409,9 @@
|
||||
37BA794E26DC3E0E002A0235 /* Int+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Format.swift"; sourceTree = "<group>"; };
|
||||
37BA796D26DC412E002A0235 /* Int+FormatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+FormatTests.swift"; sourceTree = "<group>"; };
|
||||
37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVNavigationView.swift; sourceTree = "<group>"; };
|
||||
37BBB3392B6B9053001C4845 /* ImportSettingsSheetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsSheetViewModel.swift; sourceTree = "<group>"; };
|
||||
37BBB33E2B6B9D52001C4845 /* ImportSettingsSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsSheetView.swift; sourceTree = "<group>"; };
|
||||
37BBB3422B6BB88F001C4845 /* ImportSettingsAccountRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportSettingsAccountRow.swift; sourceTree = "<group>"; };
|
||||
37BC50A72778A84700510953 /* HistorySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistorySettings.swift; sourceTree = "<group>"; };
|
||||
37BC50AB2778BCBA00510953 /* HistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HistoryModel.swift; path = Model/HistoryModel.swift; sourceTree = SOURCE_ROOT; };
|
||||
37BD07B42698AA4D003EBB87 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@ -1373,6 +1486,8 @@
|
||||
37E6D79F2944CD3800550C3D /* CacheStatusHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheStatusHeader.swift; sourceTree = "<group>"; };
|
||||
37E70922271CD43000D34DDE /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
|
||||
37E70926271CDDAE00D34DDE /* OpenSettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsButton.swift; sourceTree = "<group>"; };
|
||||
37E75CC62B6AEAF7003A6237 /* RecentlyOpenedImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentlyOpenedImporter.swift; sourceTree = "<group>"; };
|
||||
37E75CCA2B6AEB01003A6237 /* RecentlyOpenedExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentlyOpenedExporter.swift; sourceTree = "<group>"; };
|
||||
37E80F3B287B107F00561799 /* VideoDetailsOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsOverlay.swift; sourceTree = "<group>"; };
|
||||
37E80F3F287B472300561799 /* ScrollContentBackground+Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScrollContentBackground+Backport.swift"; sourceTree = "<group>"; };
|
||||
37E868FD29AA400B003128D0 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@ -1780,9 +1895,11 @@
|
||||
376418892A6FE32D008DDCC1 /* AddPublicInstanceButton.swift */,
|
||||
37F0F4ED286F734400C06C2E /* AdvancedSettings.swift */,
|
||||
376BE50A27349108009AD608 /* BrowsingSettings.swift */,
|
||||
375AC2992B66B7D600B680E7 /* ExportSettings.swift */,
|
||||
37579D5C27864F5F00FD0B98 /* Help.swift */,
|
||||
37BC50A72778A84700510953 /* HistorySettings.swift */,
|
||||
37FADFFF272ED58000330459 /* HomeSettings.swift */,
|
||||
37BBB33D2B6B9C80001C4845 /* Import */,
|
||||
37484C2426FC83E000287258 /* InstanceForm.swift */,
|
||||
37484C2C26FC844700287258 /* InstanceSettings.swift */,
|
||||
374924D92921050B0017D862 /* LocationsSettings.swift */,
|
||||
@ -1988,6 +2105,52 @@
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37A7D6E22B67E2EF009CB1ED /* Import Export Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37A7D6F12B67E433009CB1ED /* Importers */,
|
||||
37A7D6F02B67E42D009CB1ED /* Exporters */,
|
||||
372C74692B67098A00BE179B /* ImportSettingsFileModel.swift */,
|
||||
375AC29D2B66BDD600B680E7 /* ImportExportSettingsModel.swift */,
|
||||
);
|
||||
path = "Import Export Settings";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37A7D6F02B67E42D009CB1ED /* Exporters */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37A7D7222B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift */,
|
||||
37A7D6E82B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift */,
|
||||
37A7D6FA2B680822009CB1ED /* ControlsSettingsGroupExporter.swift */,
|
||||
37A7D70A2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift */,
|
||||
37A7D71A2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift */,
|
||||
37A7D72A2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift */,
|
||||
37A7D6F22B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift */,
|
||||
37A7D7022B680A97009CB1ED /* QualitySettingsGroupExporter.swift */,
|
||||
37E75CCA2B6AEB01003A6237 /* RecentlyOpenedExporter.swift */,
|
||||
37A7D6E42B67E315009CB1ED /* SettingsGroupExporter.swift */,
|
||||
37A7D7122B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift */,
|
||||
);
|
||||
path = Exporters;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37A7D6F12B67E433009CB1ED /* Importers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37A7D7262B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift */,
|
||||
37A7D6EC2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift */,
|
||||
37A7D6FE2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift */,
|
||||
37A7D70E2B680CED009CB1ED /* HistorySettingsGroupImporter.swift */,
|
||||
37A7D71E2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift */,
|
||||
37A7D72E2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift */,
|
||||
37A7D6F62B68071C009CB1ED /* PlayerSettingsGroupImporter.swift */,
|
||||
37A7D7062B680A9E009CB1ED /* QualitySettingsGroupImporter.swift */,
|
||||
37E75CC62B6AEAF7003A6237 /* RecentlyOpenedImporter.swift */,
|
||||
37A7D7162B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift */,
|
||||
);
|
||||
path = Importers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BA796C26DC4105002A0235 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1996,6 +2159,18 @@
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BBB33D2B6B9C80001C4845 /* Import */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BBB3422B6BB88F001C4845 /* ImportSettingsAccountRow.swift */,
|
||||
372C74622B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift */,
|
||||
37BBB33E2B6B9D52001C4845 /* ImportSettingsSheetView.swift */,
|
||||
37BBB3392B6B9053001C4845 /* ImportSettingsSheetViewModel.swift */,
|
||||
372C74652B67044300BE179B /* ImportSettingsSheetViewModifier.swift */,
|
||||
);
|
||||
path = Import;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BDFF1829487B74000C6404 /* Channels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2171,6 +2346,7 @@
|
||||
37D4B1B72672CFE300C925CA /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37A7D6E22B67E2EF009CB1ED /* Import Export Settings */,
|
||||
3743B86627216A1E00261544 /* Accounts */,
|
||||
3743B864272169E200261544 /* Applications */,
|
||||
377F9F79294403DC0043F856 /* Cache */,
|
||||
@ -2850,13 +3026,16 @@
|
||||
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||
37C2211D27ADA33300305B41 /* MPVViewController.swift in Sources */,
|
||||
37A362BE29537AAA00BDF328 /* PlaybackSettings.swift in Sources */,
|
||||
37A7D6F72B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */,
|
||||
371B7E612759706A00D21217 /* CommentsView.swift in Sources */,
|
||||
37D9BA0629507F69002586BD /* PlayerControlsSettings.swift in Sources */,
|
||||
37A7D7232B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */,
|
||||
3773B80A2ADC076800B5FEF3 /* RefreshControlModifier.swift in Sources */,
|
||||
379DC3D128BA4EB400B09677 /* Seek.swift in Sources */,
|
||||
371B7E6A2759791900D21217 /* CommentsModel.swift in Sources */,
|
||||
37E8B0F027B326F30024006F /* Comparable+Clamped.swift in Sources */,
|
||||
37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */,
|
||||
372C74632B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift in Sources */,
|
||||
372CFD15285F2E2A00B0B54B /* ControlsBar.swift in Sources */,
|
||||
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
|
||||
37599F38272B4D740087F250 /* FavoriteButton.swift in Sources */,
|
||||
@ -2877,12 +3056,14 @@
|
||||
37D2E0D428B67EFC00F64D52 /* Delay.swift in Sources */,
|
||||
3776925229463C310055EC18 /* PlaylistsCacheModel.swift in Sources */,
|
||||
3759234628C26C7B00C052EC /* Refreshable+Backport.swift in Sources */,
|
||||
37A7D6E92B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */,
|
||||
374924ED2921669B0017D862 /* PreferenceKeys.swift in Sources */,
|
||||
37130A5B277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
||||
37152EEA26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
3761ABFD26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */,
|
||||
377FF88F291A99580028EB0B /* HistoryView.swift in Sources */,
|
||||
3782B94F27553A6700990149 /* SearchSuggestions.swift in Sources */,
|
||||
37A7D7172B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */,
|
||||
378E50FF26FE8EEE00F49626 /* AccountViewButton.swift in Sources */,
|
||||
374924F029216C630017D862 /* VideoActions.swift in Sources */,
|
||||
37169AA62729E2CC0011DE61 /* AccountsBridge.swift in Sources */,
|
||||
@ -2897,6 +3078,7 @@
|
||||
374AB3D728BCAF0000DF56FB /* SeekModel.swift in Sources */,
|
||||
37130A5F277657300033018A /* PersistenceController.swift in Sources */,
|
||||
37FD43E32704847C0073EE42 /* View+Fixtures.swift in Sources */,
|
||||
37A7D6F32B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */,
|
||||
3776ADD6287381240078EBC4 /* Captions.swift in Sources */,
|
||||
37BA793F26DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
37C194C726F6A9C8005D3B96 /* RecentsModel.swift in Sources */,
|
||||
@ -2918,6 +3100,7 @@
|
||||
37BDFF1F29488117000C6404 /* ChannelPlaylistListItem.swift in Sources */,
|
||||
371CC76C29466F5A00979C1A /* AccountsViewModel.swift in Sources */,
|
||||
37B4E805277D0AB4004BF56A /* Orientation.swift in Sources */,
|
||||
37E75CCB2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */,
|
||||
37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */,
|
||||
37F7D82C289EB05F00E2B3D0 /* SettingsPickerModifier.swift in Sources */,
|
||||
375EC95D289EEEE000751258 /* QualityProfile.swift in Sources */,
|
||||
@ -2931,8 +3114,11 @@
|
||||
37F4AE7226828F0900BD60EA /* VerticalCells.swift in Sources */,
|
||||
376578852685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
||||
3722AEBC274DA396005EA4D6 /* Badge+Backport.swift in Sources */,
|
||||
37A7D71F2B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */,
|
||||
3748186626A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||
37599F34272B44000087F250 /* FavoritesModel.swift in Sources */,
|
||||
37A7D6FF2B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */,
|
||||
37BBB33F2B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */,
|
||||
3773B8152ADC081300B5FEF3 /* VisualEffectBlur-iOS.swift in Sources */,
|
||||
3717407D2949D40800FDDBC7 /* ChannelLinkView.swift in Sources */,
|
||||
379ACB512A1F8DB000E01914 /* HomeSettingsButton.swift in Sources */,
|
||||
@ -2959,12 +3145,16 @@
|
||||
37F13B62285E43C000B137E4 /* ControlsOverlay.swift in Sources */,
|
||||
375E45F827B1AC4700BA7902 /* PlayerControlsModel.swift in Sources */,
|
||||
37EAD86F267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||
37A7D70F2B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */,
|
||||
37A7D6FB2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */,
|
||||
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */,
|
||||
37E64DD126D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */,
|
||||
37C89322294532220032AFD3 /* PlayerOverlayModifier.swift in Sources */,
|
||||
376578892685471400D4EA09 /* Playlist.swift in Sources */,
|
||||
37A7D7272B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */,
|
||||
37B4E803277D0A72004BF56A /* AppDelegate.swift in Sources */,
|
||||
37FD77002932C4DA00D91A5F /* URL+ByReplacingYatteeProtocol.swift in Sources */,
|
||||
375AC29E2B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */,
|
||||
373CFADB269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||
37772E0D2A216F8600608BD9 /* String+ReplacingHTMLEntities.swift in Sources */,
|
||||
3714166F267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||
@ -3005,6 +3195,7 @@
|
||||
37E6D7A02944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
|
||||
374C053F272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
||||
375F7410289DC35A00747050 /* PlayerBackendView.swift in Sources */,
|
||||
37A7D70B2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */,
|
||||
37FB28412721B22200A57617 /* ContentItem.swift in Sources */,
|
||||
379F141F289ECE7F00DE48B5 /* QualitySettings.swift in Sources */,
|
||||
378E9C4029455A5800B2D696 /* ChannelsView.swift in Sources */,
|
||||
@ -3028,6 +3219,7 @@
|
||||
37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */,
|
||||
3784B23B272894DA00B09468 /* ShareSheet.swift in Sources */,
|
||||
379775932689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||
372C74662B67044300BE179B /* ImportSettingsSheetViewModifier.swift in Sources */,
|
||||
37CFB48528AFE2510070024C /* VideoDescription.swift in Sources */,
|
||||
3773B8042ADC076800B5FEF3 /* UIView+Extensions.swift in Sources */,
|
||||
37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
|
||||
@ -3042,6 +3234,7 @@
|
||||
37EBD8CA27AF26C200F1C24B /* MPVBackend.swift in Sources */,
|
||||
37635FE4291EA6CF00C11E79 /* AccentButton.swift in Sources */,
|
||||
37DCD3152A18F7630059A470 /* SafeAreaModel.swift in Sources */,
|
||||
37A7D72B2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */,
|
||||
37D526DE2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
||||
37484C2526FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||
37B767DB2677C3CA0098BAA8 /* PlayerModel.swift in Sources */,
|
||||
@ -3057,11 +3250,14 @@
|
||||
37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */,
|
||||
377E17142928265900894889 /* ListRowSeparator+Backport.swift in Sources */,
|
||||
373CFACB26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37A7D71B2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */,
|
||||
37A7D7032B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */,
|
||||
37F7AB4D28A9361F00FB46B5 /* UIDevice+Cellular.swift in Sources */,
|
||||
37141673267A8E10006CA35D /* Country.swift in Sources */,
|
||||
37FEF11327EFD8580033912F /* PlaceholderCell.swift in Sources */,
|
||||
37B2631A2735EAAB00FE0D40 /* FavoriteResourceObserver.swift in Sources */,
|
||||
37A362C229537FED00BDF328 /* PlaybackSettingsPresentationDetents+Backport.swift in Sources */,
|
||||
37BBB33A2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */,
|
||||
3748186E26A769D60084E870 /* DetailBadge.swift in Sources */,
|
||||
3744A96028B99ADD005DE0A7 /* PlayerControlsLayout.swift in Sources */,
|
||||
376BE50B27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
||||
@ -3069,7 +3265,9 @@
|
||||
37F0F4EE286F734400C06C2E /* AdvancedSettings.swift in Sources */,
|
||||
37AAF2A026741C97007FC770 /* FeedView.swift in Sources */,
|
||||
374924E3292141320017D862 /* InspectorView.swift in Sources */,
|
||||
37A7D6ED2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */,
|
||||
37EF5C222739D37B00B03725 /* MenuModel.swift in Sources */,
|
||||
37A7D7072B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */,
|
||||
37599F30272B42810087F250 /* FavoriteItem.swift in Sources */,
|
||||
374924E729215FB60017D862 /* TapRecognizerViewModifier.swift in Sources */,
|
||||
3773B8072ADC076800B5FEF3 /* UIResponder+Extensions.swift in Sources */,
|
||||
@ -3087,6 +3285,7 @@
|
||||
37D4B19726717E1500C925CA /* Video.swift in Sources */,
|
||||
37484C2926FC83FF00287258 /* AccountForm.swift in Sources */,
|
||||
37E70927271CDDAE00D34DDE /* OpenSettingsButton.swift in Sources */,
|
||||
37A7D7132B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */,
|
||||
371F2F1A269B43D300E4A7AB /* NavigationModel.swift in Sources */,
|
||||
37BA221129526A19000DAD1F /* ControlsGradientView.swift in Sources */,
|
||||
377ABC44286E4B74009C986F /* ManifestedInstance.swift in Sources */,
|
||||
@ -3094,9 +3293,13 @@
|
||||
375E45F527B1976B00BA7902 /* MPVOGLView.swift in Sources */,
|
||||
37BE0BCF26A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||
377692562946476F0055EC18 /* ChannelPlaylistsCacheModel.swift in Sources */,
|
||||
37E75CC72B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */,
|
||||
3769C02E2779F18600DDB3EA /* PlaceholderProgressView.swift in Sources */,
|
||||
375AC29A2B66B7D600B680E7 /* ExportSettings.swift in Sources */,
|
||||
37B7CFE92A19603B001B0564 /* ToolbarBackground+Backport.swift in Sources */,
|
||||
37DCD3172A191A180059A470 /* AVPlayerViewController+FullScreen.swift in Sources */,
|
||||
372C746A2B67098A00BE179B /* ImportSettingsFileModel.swift in Sources */,
|
||||
37A7D6E52B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */,
|
||||
379EF9E029AA585F009FE6C6 /* HideShortsButtons.swift in Sources */,
|
||||
37F5E8B6291BE9D0006C15F5 /* URLBookmarkModel.swift in Sources */,
|
||||
37579D5D27864F5F00FD0B98 /* Help.swift in Sources */,
|
||||
@ -3107,6 +3310,7 @@
|
||||
37001563271B1F250049C794 /* AccountsModel.swift in Sources */,
|
||||
3795593627B08538007FF8F4 /* StreamControl.swift in Sources */,
|
||||
37B7CFEB2A1960EC001B0564 /* ToolbarColorScheme+Backport.swift in Sources */,
|
||||
37BBB3432B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */,
|
||||
37A2B346294723850050933E /* CacheModel.swift in Sources */,
|
||||
37CC3F50270D010D00608308 /* VideoBanner.swift in Sources */,
|
||||
378E50FB26FE8B9F00F49626 /* Instance.swift in Sources */,
|
||||
@ -3127,6 +3331,7 @@
|
||||
377F9F7B294403F20043F856 /* VideosCacheModel.swift in Sources */,
|
||||
37B795902771DAE0001CF27B /* OpenURLHandler.swift in Sources */,
|
||||
37732FF02703A26300F04329 /* AccountValidationStatus.swift in Sources */,
|
||||
37A7D72F2B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -3151,6 +3356,8 @@
|
||||
371CC77129468BDC00979C1A /* SettingsButtons.swift in Sources */,
|
||||
37CEE4BE2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||
3703100327B0713600ECDDAA /* PlayerGestures.swift in Sources */,
|
||||
37A7D7282B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */,
|
||||
37A7D72C2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */,
|
||||
374C0540272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
||||
3738535529451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */,
|
||||
379F1420289ECE7F00DE48B5 /* QualitySettings.swift in Sources */,
|
||||
@ -3187,6 +3394,7 @@
|
||||
379E7C342A20FE3900AF8118 /* FocusableSearchTextField.swift in Sources */,
|
||||
37F5C7E12A1E2AF300927B73 /* ListView.swift in Sources */,
|
||||
37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */,
|
||||
37E75CCC2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */,
|
||||
3784CDE327772EE40055BBF2 /* Watch.swift in Sources */,
|
||||
371AC0B7294D1D6E0085989E /* PlayingIndicatorView.swift in Sources */,
|
||||
3773B8182ADC081300B5FEF3 /* VisualEffectBlur-macOS.swift in Sources */,
|
||||
@ -3199,14 +3407,17 @@
|
||||
3756C2A72861131100E4B059 /* NetworkState.swift in Sources */,
|
||||
37D6025A28C17375009E8D98 /* PlaybackStatsView.swift in Sources */,
|
||||
3729037F2739E47400EA99F6 /* MenuCommands.swift in Sources */,
|
||||
37BBB3442B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */,
|
||||
3763C98A290C7A50004D3B5F /* OpenVideosView.swift in Sources */,
|
||||
37C0698327260B2100F7F6CB /* ThumbnailsModel.swift in Sources */,
|
||||
371CC7752946963000979C1A /* ListingStyleButtons.swift in Sources */,
|
||||
374AB3DC28BCAF7E00DF56FB /* SeekType.swift in Sources */,
|
||||
376B2E0826F920D600B1D64D /* SignInRequiredView.swift in Sources */,
|
||||
37E75CC82B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */,
|
||||
37CC3F4D270CFE1700608308 /* PlayerQueueView.swift in Sources */,
|
||||
37B81B0026D2CA3700675966 /* VideoDetails.swift in Sources */,
|
||||
3752069A285E8DD300CA655F /* Chapter.swift in Sources */,
|
||||
37A7D6EE2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */,
|
||||
373EBD69291F252D002ADB9C /* HomeSettings.swift in Sources */,
|
||||
37B7CFEE2A19789F001B0564 /* MacOSPiPDelegate.swift in Sources */,
|
||||
37484C1A26FC837400287258 /* PlayerSettings.swift in Sources */,
|
||||
@ -3215,14 +3426,17 @@
|
||||
37484C3226FCB8F900287258 /* AccountValidator.swift in Sources */,
|
||||
37D9BA0729507F69002586BD /* PlayerControlsSettings.swift in Sources */,
|
||||
378E9C4129455A5800B2D696 /* ChannelsView.swift in Sources */,
|
||||
375AC29B2B66B7D600B680E7 /* ExportSettings.swift in Sources */,
|
||||
378AE944274EF00A006A4EE1 /* Color+Background.swift in Sources */,
|
||||
37F49BA426CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
||||
37EAD870267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||
3788AC2826F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
||||
377FF88C291A60310028EB0B /* OpenVideosModel.swift in Sources */,
|
||||
378AE93A274EDFAF006A4EE1 /* Badge+Backport.swift in Sources */,
|
||||
37A7D6F42B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */,
|
||||
3773B8162ADC081300B5FEF3 /* VisualEffectBlur-iOS.swift in Sources */,
|
||||
37599F35272B44000087F250 /* FavoritesModel.swift in Sources */,
|
||||
372C74672B67044300BE179B /* ImportSettingsSheetViewModifier.swift in Sources */,
|
||||
376527BC285F60F700102284 /* PlayerTimeModel.swift in Sources */,
|
||||
37F64FE526FE70A60081B69E /* RedrawOnModifier.swift in Sources */,
|
||||
377ABC45286E4B74009C986F /* ManifestedInstance.swift in Sources */,
|
||||
@ -3239,9 +3453,11 @@
|
||||
378E510026FE8EEE00F49626 /* AccountViewButton.swift in Sources */,
|
||||
370F4FA927CC163A001B35DC /* PlayerBackend.swift in Sources */,
|
||||
37141670267A8ACC006CA35D /* TrendingView.swift in Sources */,
|
||||
37A7D7002B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */,
|
||||
379DC3D228BA4EB400B09677 /* Seek.swift in Sources */,
|
||||
376BE50727347B57009AD608 /* SettingsHeader.swift in Sources */,
|
||||
378AE93C274EDFB2006A4EE1 /* Backport.swift in Sources */,
|
||||
37BBB3402B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */,
|
||||
37A2B347294723850050933E /* CacheModel.swift in Sources */,
|
||||
37152EEB26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
37F4AD2028612DFD004D0F66 /* Buffering.swift in Sources */,
|
||||
@ -3276,6 +3492,7 @@
|
||||
373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37AAF29126740715007FC770 /* Channel.swift in Sources */,
|
||||
37F4AD1C28612B23004D0F66 /* OpeningStream.swift in Sources */,
|
||||
37A7D6EA2B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */,
|
||||
376A33E12720CAD6000C1D6B /* VideosApp.swift in Sources */,
|
||||
37BD07BC2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
||||
37579D5E27864F5F00FD0B98 /* Help.swift in Sources */,
|
||||
@ -3286,18 +3503,23 @@
|
||||
377E17152928265900894889 /* ListRowSeparator+Backport.swift in Sources */,
|
||||
371CC76929466ED000979C1A /* AccountsView.swift in Sources */,
|
||||
37C3A242272359900087A57A /* Double+Format.swift in Sources */,
|
||||
37A7D7302B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */,
|
||||
37A7D6FC2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */,
|
||||
37B795912771DAE0001CF27B /* OpenURLHandler.swift in Sources */,
|
||||
37EFAC0928C138CD00ED9B89 /* ControlsOverlayModel.swift in Sources */,
|
||||
37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */,
|
||||
376578922685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
||||
37484C2626FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||
3751BA7E27E63F1D007B1A60 /* MPVOGLView.swift in Sources */,
|
||||
37BBB33B2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */,
|
||||
377FC7E4267A084E00A6BBAF /* SearchView.swift in Sources */,
|
||||
37B81AFA26D2C9A700675966 /* VideoPlayerSizeModifier.swift in Sources */,
|
||||
37A7D6E62B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */,
|
||||
377A20AA2693C9A2002842B8 /* TypedContentAccessors.swift in Sources */,
|
||||
376A33E52720CB35000C1D6B /* Account.swift in Sources */,
|
||||
376578862685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
||||
37A9965F26D6F9B9006E3224 /* HomeView.swift in Sources */,
|
||||
37A7D7142B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */,
|
||||
37F4AE7326828F0900BD60EA /* VerticalCells.swift in Sources */,
|
||||
37001560271B12DD0049C794 /* SiestaConfiguration.swift in Sources */,
|
||||
372D85DE283841B800FF3C7D /* PiPDelegate.swift in Sources */,
|
||||
@ -3306,14 +3528,18 @@
|
||||
37C8E702294FC97D00EEAB14 /* QueueView.swift in Sources */,
|
||||
37C0697F2725C8D400F7F6CB /* CMTime+DefaultTimescale.swift in Sources */,
|
||||
379ACB522A1F8DB000E01914 /* HomeSettingsButton.swift in Sources */,
|
||||
37A7D7082B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */,
|
||||
37A9965B26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||
37E6D79D2944AE1A00550C3D /* FeedModel.swift in Sources */,
|
||||
375AC29F2B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */,
|
||||
37732FF52703D32400F04329 /* Sidebar.swift in Sources */,
|
||||
379775942689365600DD52A8 /* Array+Next.swift in Sources */,
|
||||
37A7D7102B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */,
|
||||
377ABC49286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
||||
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
|
||||
3784B23E2728B85300B09468 /* ShareButton.swift in Sources */,
|
||||
375F7411289DC35A00747050 /* PlayerBackendView.swift in Sources */,
|
||||
37A7D70C2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */,
|
||||
37FEF11427EFD8580033912F /* PlaceholderCell.swift in Sources */,
|
||||
37E64DD226D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */,
|
||||
37F0F4EF286F734400C06C2E /* AdvancedSettings.swift in Sources */,
|
||||
@ -3330,7 +3556,9 @@
|
||||
3703100027B04DCC00ECDDAA /* PlayerControls.swift in Sources */,
|
||||
37130A5C277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
||||
37FD43E42704847C0073EE42 /* View+Fixtures.swift in Sources */,
|
||||
37A7D7242B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */,
|
||||
37C069782725962F00F7F6CB /* ScreenSaverManager.swift in Sources */,
|
||||
372C74642B66FFFC00BE179B /* ImportSettingsFileImporterViewModifier.swift in Sources */,
|
||||
37AAF2A126741C97007FC770 /* FeedView.swift in Sources */,
|
||||
37F4AD2728613B81004D0F66 /* Color+Debug.swift in Sources */,
|
||||
37732FF12703A26300F04329 /* AccountValidationStatus.swift in Sources */,
|
||||
@ -3346,6 +3574,7 @@
|
||||
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||
371B7E5D27596B8400D21217 /* Comment.swift in Sources */,
|
||||
37EF5C232739D37B00B03725 /* MenuModel.swift in Sources */,
|
||||
37A7D71C2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */,
|
||||
37BC50A92778A84700510953 /* HistorySettings.swift in Sources */,
|
||||
374DE88128BB896C0062BBF2 /* PlayerDragGesture.swift in Sources */,
|
||||
37599F31272B42810087F250 /* FavoriteItem.swift in Sources */,
|
||||
@ -3353,9 +3582,11 @@
|
||||
37E04C0F275940FB00172673 /* VerticalScrollingFix.swift in Sources */,
|
||||
377F9F7C294403F20043F856 /* VideosCacheModel.swift in Sources */,
|
||||
374924E4292141320017D862 /* InspectorView.swift in Sources */,
|
||||
37A7D7202B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */,
|
||||
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */,
|
||||
37D526DF2720AC4400ED2F5E /* VideosAPI.swift in Sources */,
|
||||
3764188B2A6FE32D008DDCC1 /* AddPublicInstanceButton.swift in Sources */,
|
||||
37A7D7182B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */,
|
||||
377F9F802944175F0043F856 /* FeedCacheModel.swift in Sources */,
|
||||
373C8FE5275B955100CB5936 /* CommentsPage.swift in Sources */,
|
||||
37D4B0E52671614900C925CA /* YatteeApp.swift in Sources */,
|
||||
@ -3367,8 +3598,10 @@
|
||||
37BA794026DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
37BDFF2029488117000C6404 /* ChannelPlaylistListItem.swift in Sources */,
|
||||
3711404026B206A6005B3555 /* SearchModel.swift in Sources */,
|
||||
37A7D7042B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */,
|
||||
37484C2A26FC83FF00287258 /* AccountForm.swift in Sources */,
|
||||
37BE0BD026A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||
372C746B2B67098A00BE179B /* ImportSettingsFileModel.swift in Sources */,
|
||||
373CFAEC26975CBF003CB2C6 /* PlaylistFormView.swift in Sources */,
|
||||
37D2E0D528B67EFC00F64D52 /* Delay.swift in Sources */,
|
||||
37977584268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
||||
@ -3383,6 +3616,7 @@
|
||||
3743B86927216D3600261544 /* ChannelCell.swift in Sources */,
|
||||
3748186B26A764FB0084E870 /* Thumbnail+Fixtures.swift in Sources */,
|
||||
3782B95E2755858100990149 /* NSTextField+FocusRingType.swift in Sources */,
|
||||
37A7D6F82B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */,
|
||||
37C3A252272366440087A57A /* ChannelPlaylistView.swift in Sources */,
|
||||
3754B01628B7F84D009717C8 /* Constants.swift in Sources */,
|
||||
37270F1D28E06E3E00856150 /* String+Localizable.swift in Sources */,
|
||||
@ -3470,10 +3704,12 @@
|
||||
37579D5F27864F5F00FD0B98 /* Help.swift in Sources */,
|
||||
370015AB28BBAE7F000149FD /* ProgressBar.swift in Sources */,
|
||||
375EC95F289EEEE000751258 /* QualityProfile.swift in Sources */,
|
||||
37A7D7112B680CED009CB1ED /* HistorySettingsGroupImporter.swift in Sources */,
|
||||
37EAD871267B9ED100D9E01B /* Segment.swift in Sources */,
|
||||
373C8FE6275B955100CB5936 /* CommentsPage.swift in Sources */,
|
||||
375EC974289F2ABF00751258 /* MultiselectRow.swift in Sources */,
|
||||
37EBD8C827AF26B300F1C24B /* AVPlayerBackend.swift in Sources */,
|
||||
37BBB3452B6BB88F001C4845 /* ImportSettingsAccountRow.swift in Sources */,
|
||||
378E9C3E2945565500B2D696 /* SubscriptionsView.swift in Sources */,
|
||||
37EFAC0A28C138CD00ED9B89 /* ControlsOverlayModel.swift in Sources */,
|
||||
37CC3F52270D010D00608308 /* VideoBanner.swift in Sources */,
|
||||
@ -3496,11 +3732,15 @@
|
||||
376578872685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
|
||||
37BDFF1D29487C5A000C6404 /* ChannelListItem.swift in Sources */,
|
||||
37D4B1802671650A00C925CA /* YatteeApp.swift in Sources */,
|
||||
37A7D6E32B67E303009CB1ED /* ImportSettingsFileModel.swift in Sources */,
|
||||
37A7D7152B680D62009CB1ED /* SponsorBlockSettingsGroupExporter.swift in Sources */,
|
||||
37BBB33C2B6B9053001C4845 /* ImportSettingsSheetViewModel.swift in Sources */,
|
||||
3748187026A769D60084E870 /* DetailBadge.swift in Sources */,
|
||||
3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */,
|
||||
371B7E632759706A00D21217 /* CommentsView.swift in Sources */,
|
||||
37D6025D28C17719009E8D98 /* ControlsOverlayButton.swift in Sources */,
|
||||
37D2E0D628B67EFC00F64D52 /* Delay.swift in Sources */,
|
||||
37A7D70D2B680CE6009CB1ED /* HistorySettingsGroupExporter.swift in Sources */,
|
||||
379F1421289ECE7F00DE48B5 /* QualitySettings.swift in Sources */,
|
||||
37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
|
||||
371CC76A29466ED000979C1A /* AccountsView.swift in Sources */,
|
||||
@ -3511,6 +3751,8 @@
|
||||
37BD07C92698FBDB003EBB87 /* ContentView.swift in Sources */,
|
||||
37BDFF2129488117000C6404 /* ChannelPlaylistListItem.swift in Sources */,
|
||||
37BC50AA2778A84700510953 /* HistorySettings.swift in Sources */,
|
||||
37A7D6E72B67E315009CB1ED /* SettingsGroupExporter.swift in Sources */,
|
||||
375AC2A02B66BDD600B680E7 /* ImportExportSettingsModel.swift in Sources */,
|
||||
37F13B64285E43C000B137E4 /* ControlsOverlay.swift in Sources */,
|
||||
376B2E0926F920D600B1D64D /* SignInRequiredView.swift in Sources */,
|
||||
378FFBC628660172009E3FBE /* URLParser.swift in Sources */,
|
||||
@ -3521,6 +3763,8 @@
|
||||
3788AC2926F6840700F6BAA9 /* FavoriteItemView.swift in Sources */,
|
||||
37319F0727103F94004ECCD0 /* PlayerQueue.swift in Sources */,
|
||||
3718B9A52921A97F0003DB2E /* InspectorView.swift in Sources */,
|
||||
37A7D72D2B68100A009CB1ED /* OtherDataSettingsGroupExporter.swift in Sources */,
|
||||
372C74682B67044900BE179B /* ImportSettingsSheetViewModifier.swift in Sources */,
|
||||
37E70925271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
||||
376BE50D27349108009AD608 /* BrowsingSettings.swift in Sources */,
|
||||
37CFB48728AFE2510070024C /* VideoDescription.swift in Sources */,
|
||||
@ -3534,12 +3778,14 @@
|
||||
37BA794126DB8F97002A0235 /* ChannelVideosView.swift in Sources */,
|
||||
371CC77229468BDC00979C1A /* SettingsButtons.swift in Sources */,
|
||||
37C0697C2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
|
||||
375AC29C2B66B7D600B680E7 /* ExportSettings.swift in Sources */,
|
||||
3718B9A12921A9640003DB2E /* VideoDetails.swift in Sources */,
|
||||
378AE93D274EDFB3006A4EE1 /* Backport.swift in Sources */,
|
||||
377F9F812944175F0043F856 /* FeedCacheModel.swift in Sources */,
|
||||
37130A5D277657090033018A /* Yattee.xcdatamodeld in Sources */,
|
||||
37C3A243272359900087A57A /* Double+Format.swift in Sources */,
|
||||
37AAF29226740715007FC770 /* Channel.swift in Sources */,
|
||||
37A7D7092B680A9E009CB1ED /* QualitySettingsGroupImporter.swift in Sources */,
|
||||
37EAD86D267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||
376527BD285F60F700102284 /* PlayerTimeModel.swift in Sources */,
|
||||
377692582946476F0055EC18 /* ChannelPlaylistsCacheModel.swift in Sources */,
|
||||
@ -3562,9 +3808,11 @@
|
||||
3765788B2685471400D4EA09 /* Playlist.swift in Sources */,
|
||||
376A33E22720CAD6000C1D6B /* VideosApp.swift in Sources */,
|
||||
373CFADD269663F1003CB2C6 /* Thumbnail.swift in Sources */,
|
||||
37A7D7012B68082F009CB1ED /* ControlsSettingsGroupImporter.swift in Sources */,
|
||||
376B0562293FF45F0062AC78 /* PeerTubeAPI.swift in Sources */,
|
||||
3738535629451DC800D2D0CB /* BookmarksCacheModel.swift in Sources */,
|
||||
37E64DD326D597EB00C71877 /* SubscribedChannelsModel.swift in Sources */,
|
||||
37A7D6F92B68071C009CB1ED /* PlayerSettingsGroupImporter.swift in Sources */,
|
||||
3752069B285E8DD300CA655F /* Chapter.swift in Sources */,
|
||||
37B044B926F7AB9000E1419D /* SettingsView.swift in Sources */,
|
||||
3743B86A27216D3600261544 /* ChannelCell.swift in Sources */,
|
||||
@ -3576,12 +3824,14 @@
|
||||
37192D5928B179D60012EEDD /* ChaptersView.swift in Sources */,
|
||||
37B767DD2677C3CA0098BAA8 /* PlayerModel.swift in Sources */,
|
||||
373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||
37A7D6FD2B680822009CB1ED /* ControlsSettingsGroupExporter.swift in Sources */,
|
||||
37C89324294532220032AFD3 /* PlayerOverlayModifier.swift in Sources */,
|
||||
3784CDE427772EE40055BBF2 /* Watch.swift in Sources */,
|
||||
3730D8A02712E2B70020ED53 /* NowPlayingView.swift in Sources */,
|
||||
37169AA42729D98A0011DE61 /* InstancesBridge.swift in Sources */,
|
||||
37D4B18E26717B3800C925CA /* VideoCell.swift in Sources */,
|
||||
375E45F627B1976B00BA7902 /* MPVOGLView.swift in Sources */,
|
||||
37A7D7052B680A97009CB1ED /* QualitySettingsGroupExporter.swift in Sources */,
|
||||
375EC95B289EEB8200751258 /* QualityProfileForm.swift in Sources */,
|
||||
371B7E682759786B00D21217 /* Comment+Fixtures.swift in Sources */,
|
||||
37BE0BD126A0E2D50092E2DB /* VideoPlayerView.swift in Sources */,
|
||||
@ -3594,6 +3844,7 @@
|
||||
372820402945E4A8009A0E2D /* SubscriptionsPageButton.swift in Sources */,
|
||||
37001565271B1F250049C794 /* AccountsModel.swift in Sources */,
|
||||
3751B4B427836902000B7DF4 /* SearchPage.swift in Sources */,
|
||||
37A7D7252B680F6F009CB1ED /* AdvancedSettingsGroupExporter.swift in Sources */,
|
||||
377ABC46286E4B74009C986F /* ManifestedInstance.swift in Sources */,
|
||||
37BA221329526A19000DAD1F /* ControlsGradientView.swift in Sources */,
|
||||
374C0541272472C0009BDDBE /* PlayerSponsorBlock.swift in Sources */,
|
||||
@ -3609,6 +3860,7 @@
|
||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
|
||||
3756C2A82861131100E4B059 /* NetworkState.swift in Sources */,
|
||||
376578932685490700D4EA09 /* PlaylistsView.swift in Sources */,
|
||||
37E75CCD2B6AEB01003A6237 /* RecentlyOpenedExporter.swift in Sources */,
|
||||
377FF891291A99580028EB0B /* HistoryView.swift in Sources */,
|
||||
37CC3F47270CE30600608308 /* PlayerQueueItem.swift in Sources */,
|
||||
37001561271B12DD0049C794 /* SiestaConfiguration.swift in Sources */,
|
||||
@ -3624,6 +3876,7 @@
|
||||
37C3A253272366440087A57A /* ChannelPlaylistView.swift in Sources */,
|
||||
378AE945274EF00A006A4EE1 /* Color+Background.swift in Sources */,
|
||||
3743CA54270F284F00E4D32B /* View+Borders.swift in Sources */,
|
||||
37A7D6F52B67E44F009CB1ED /* PlayerSettingsGroupExporter.swift in Sources */,
|
||||
371F2F1C269B43D300E4A7AB /* NavigationModel.swift in Sources */,
|
||||
37BA794526DBA973002A0235 /* PlaylistsModel.swift in Sources */,
|
||||
37B17DA0268A1F89006AEE9B /* VideoContextMenuView.swift in Sources */,
|
||||
@ -3673,6 +3926,7 @@
|
||||
37141675267A8E10006CA35D /* Country.swift in Sources */,
|
||||
370F500C27CC1821001B35DC /* MPVViewController.swift in Sources */,
|
||||
3782B9542755667600990149 /* String+Format.swift in Sources */,
|
||||
37E75CC92B6AEAF7003A6237 /* RecentlyOpenedImporter.swift in Sources */,
|
||||
3764188C2A6FE32D008DDCC1 /* AddPublicInstanceButton.swift in Sources */,
|
||||
37D836BE294927E700005E5E /* ChannelsCacheModel.swift in Sources */,
|
||||
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
|
||||
@ -3680,11 +3934,14 @@
|
||||
37EF9A78275BEB8E0043B585 /* CommentView.swift in Sources */,
|
||||
379ACB4E2A1F8A4100E01914 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift in Sources */,
|
||||
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
|
||||
37A7D7292B680F75009CB1ED /* AdvancedSettingsGroupImporter.swift in Sources */,
|
||||
37F5C7E22A1E2AF300927B73 /* ListView.swift in Sources */,
|
||||
37BBB3412B6B9D52001C4845 /* ImportSettingsSheetView.swift in Sources */,
|
||||
37E6D7A22944CD3800550C3D /* CacheStatusHeader.swift in Sources */,
|
||||
37F49BA826CB0FCE00304AC0 /* PlaylistFormView.swift in Sources */,
|
||||
37F0F4F0286F734400C06C2E /* AdvancedSettings.swift in Sources */,
|
||||
373197DA2732060100EF734F /* RelatedView.swift in Sources */,
|
||||
37A7D71D2B680E66009CB1ED /* LocationsSettingsGroupExporter.swift in Sources */,
|
||||
37DD9DA52785BBC900539416 /* NoCommentsView.swift in Sources */,
|
||||
377ABC4A286E5887009C986F /* Sequence+Unique.swift in Sources */,
|
||||
37E6D79E2944AE1A00550C3D /* FeedModel.swift in Sources */,
|
||||
@ -3701,6 +3958,7 @@
|
||||
37E084AD2753D95F00039B7D /* AccountsNavigationLink.swift in Sources */,
|
||||
371B7E6C2759791900D21217 /* CommentsModel.swift in Sources */,
|
||||
374AB3D928BCAF0000DF56FB /* SeekModel.swift in Sources */,
|
||||
37A7D7212B680E6B009CB1ED /* LocationsSettingsGroupImporter.swift in Sources */,
|
||||
375E45F927B1AC4700BA7902 /* PlayerControlsModel.swift in Sources */,
|
||||
371CC7762946963000979C1A /* ListingStyleButtons.swift in Sources */,
|
||||
3782B95627557E4E00990149 /* SearchView.swift in Sources */,
|
||||
@ -3709,15 +3967,19 @@
|
||||
3718B9A02921A9620003DB2E /* VideoDetailsOverlay.swift in Sources */,
|
||||
377E17162928265900894889 /* ListRowSeparator+Backport.swift in Sources */,
|
||||
37FB28432721B22200A57617 /* ContentItem.swift in Sources */,
|
||||
37A7D6EF2B67E3BF009CB1ED /* BrowsingSettingsGroupImporter.swift in Sources */,
|
||||
37D2E0D228B67DBC00F64D52 /* AnimationCompletionObserverModifier.swift in Sources */,
|
||||
37A7D6EB2B67E334009CB1ED /* BrowsingSettingsGroupExporter.swift in Sources */,
|
||||
37AAF2A226741C97007FC770 /* FeedView.swift in Sources */,
|
||||
37484C1B26FC837400287258 /* PlayerSettings.swift in Sources */,
|
||||
3773B8122ADC076800B5FEF3 /* ScrollViewMatcher.swift in Sources */,
|
||||
372915E82687E3B900F5A35B /* Defaults.swift in Sources */,
|
||||
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */,
|
||||
3718B9A62921A9BE0003DB2E /* PreferenceKeys.swift in Sources */,
|
||||
37A7D7192B680D6C009CB1ED /* SponsorBlockSettingsGroupImporter.swift in Sources */,
|
||||
3797758D2689345500DD52A8 /* Store.swift in Sources */,
|
||||
37484C2F26FC844700287258 /* InstanceSettings.swift in Sources */,
|
||||
37A7D7312B681011009CB1ED /* OtherDataSettingsGroupImporter.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -34,6 +34,16 @@
|
||||
<string>public.file-url</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Settings text</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
@ -68,5 +78,31 @@
|
||||
</array>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Yattee Settings</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>stream.yattee.app-settings</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>yatteesettings</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -16,6 +16,18 @@
|
||||
<string>public.mpeg-4</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Settings</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
@ -37,5 +49,51 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Yattee Settings</string>
|
||||
<key>UTTypeIcons</key>
|
||||
<dict/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>stream.yattee.app-settings</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>yatteesettings</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.json</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Yattee Settings</string>
|
||||
<key>UTTypeIcons</key>
|
||||
<dict/>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>stream.yattee.app-settings</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>yatteesettings</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
Loading…
Reference in New Issue
Block a user