mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 13:33:42 +00:00
Open videos via URL scheme
This commit is contained in:
parent
8e0af22b94
commit
60c7027429
@ -27,17 +27,20 @@ extension PlayerModel {
|
||||
}
|
||||
}
|
||||
|
||||
func playNow(_ video: Video) {
|
||||
func playNow(_ video: Video, at time: TimeInterval? = nil) {
|
||||
addCurrentItemToHistory()
|
||||
|
||||
enqueueVideo(video, prepending: true) { _, item in
|
||||
self.advanceToItem(item)
|
||||
self.advanceToItem(item, at: time)
|
||||
}
|
||||
}
|
||||
|
||||
func playItem(_ item: PlayerQueueItem, video: Video? = nil) {
|
||||
func playItem(_ item: PlayerQueueItem, video: Video? = nil, at time: TimeInterval? = nil) {
|
||||
currentItem = item
|
||||
if currentItem.playbackTime.isNil {
|
||||
|
||||
if !time.isNil {
|
||||
currentItem.playbackTime = CMTime(seconds: time!, preferredTimescale: 1_000_000)
|
||||
} else if currentItem.playbackTime.isNil {
|
||||
currentItem.playbackTime = .zero
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ extension PlayerModel {
|
||||
currentItem.video = video!
|
||||
}
|
||||
|
||||
playVideo(currentVideo!, time: item.playbackTime)
|
||||
playVideo(currentVideo!, time: currentItem.playbackTime)
|
||||
}
|
||||
|
||||
func advanceToNextItem() {
|
||||
@ -56,12 +59,12 @@ extension PlayerModel {
|
||||
}
|
||||
}
|
||||
|
||||
func advanceToItem(_ newItem: PlayerQueueItem) {
|
||||
func advanceToItem(_ newItem: PlayerQueueItem, at time: TimeInterval? = nil) {
|
||||
addCurrentItemToHistory()
|
||||
|
||||
let item = remove(newItem)!
|
||||
loadDetails(newItem.video) { video in
|
||||
self.playItem(item, video: video)
|
||||
self.playItem(item, video: video, at: time)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,14 +32,16 @@ extension PlayerModel {
|
||||
|
||||
private func skip(_ segment: Segment, at time: CMTime) {
|
||||
guard segment.endTime.seconds <= playerItemDuration?.seconds ?? .infinity else {
|
||||
logger.error("item time is: \(time.seconds) and trying to skip to \(playerItemDuration?.seconds ?? .infinity)")
|
||||
logger.error(
|
||||
"segment end time is: \(segment.end) when player item duration is: \(playerItemDuration?.seconds ?? .infinity)"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
player.seek(to: segment.endTime)
|
||||
lastSkipped = segment
|
||||
segmentRestorationTime = time
|
||||
logger.info("SponsorBlock skipping to: \(segment.endTime)")
|
||||
logger.info("SponsorBlock skipping to: \(segment.end)")
|
||||
}
|
||||
|
||||
private func shouldSkip(_ segment: Segment, at time: CMTime) -> Bool {
|
||||
|
@ -5,10 +5,10 @@ import Logging
|
||||
import SwiftyJSON
|
||||
|
||||
final class SponsorBlockAPI: ObservableObject {
|
||||
let logger = Logger(label: "net.yattee.app.sb")
|
||||
|
||||
static let categories = ["sponsor", "selfpromo", "intro", "outro", "interaction", "music_offtopic"]
|
||||
|
||||
let logger = Logger(label: "net.yattee.app.sb")
|
||||
|
||||
@Published var videoID: String?
|
||||
@Published var segments = [Segment]()
|
||||
|
||||
|
@ -121,6 +121,7 @@
|
||||
374C053F272472C0009BDDBE /* PlayerSegments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374C053E272472C0009BDDBE /* PlayerSegments.swift */; };
|
||||
374C0540272472C0009BDDBE /* PlayerSegments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374C053E272472C0009BDDBE /* PlayerSegments.swift */; };
|
||||
374C0541272472C0009BDDBE /* PlayerSegments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374C053E272472C0009BDDBE /* PlayerSegments.swift */; };
|
||||
374C0543272496E4009BDDBE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374C0542272496E4009BDDBE /* AppDelegate.swift */; };
|
||||
375168D62700FAFF008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
375168D72700FDB8008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
375168D82700FDB9008F96A6 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375168D52700FAFF008F96A6 /* Debounce.swift */; };
|
||||
@ -247,8 +248,6 @@
|
||||
37BA794F26DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA795026DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA795126DC3E0E002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BA796F26DC412E002A0235 /* Int+FormatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA796D26DC412E002A0235 /* Int+FormatTests.swift */; };
|
||||
37BA797026DC426B002A0235 /* Int+Format.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BA794E26DC3E0E002A0235 /* Int+Format.swift */; };
|
||||
37BAB54C269B39FD00E75ED1 /* TVNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */; };
|
||||
37BADCA52699FB72009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA42699FB72009BE4FB /* Alamofire */; };
|
||||
37BADCA7269A552E009BE4FB /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37BADCA6269A552E009BE4FB /* Alamofire */; };
|
||||
@ -296,6 +295,10 @@
|
||||
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||
37C7A1D7267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */; };
|
||||
37C7A1DA267CACF50010EAD6 /* TrendingCountry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3705B17F267B4DFB00704544 /* TrendingCountry.swift */; };
|
||||
37CB12792724C76D00213B45 /* VideoURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB12782724C76D00213B45 /* VideoURLParser.swift */; };
|
||||
37CB127A2724C76D00213B45 /* VideoURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB12782724C76D00213B45 /* VideoURLParser.swift */; };
|
||||
37CB128B2724CC1F00213B45 /* VideoURLParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB127B2724C79D00213B45 /* VideoURLParserTests.swift */; };
|
||||
37CB128C2724CC8400213B45 /* VideoURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB12782724C76D00213B45 /* VideoURLParser.swift */; };
|
||||
37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; };
|
||||
37CC3F46270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; };
|
||||
37CC3F47270CE30600608308 /* PlayerQueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC3F44270CE30600608308 /* PlayerQueueItem.swift */; };
|
||||
@ -384,13 +387,6 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
37BA796726DC40CB002A0235 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 37D4B0CE2671614900C925CA;
|
||||
remoteInfo = "Pearvidious (macOS)";
|
||||
};
|
||||
37D4B0D52671614900C925CA /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 37D4B0BD2671614700C925CA /* Project object */;
|
||||
@ -447,6 +443,8 @@
|
||||
374C053427242D9F009BDDBE /* ServicesSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesSettings.swift; sourceTree = "<group>"; };
|
||||
374C053A2724614F009BDDBE /* PlayerTVMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTVMenu.swift; sourceTree = "<group>"; };
|
||||
374C053E272472C0009BDDBE /* PlayerSegments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSegments.swift; sourceTree = "<group>"; };
|
||||
374C0542272496E4009BDDBE /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = macOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
|
||||
374C0544272496FD009BDDBE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
375168D52700FAFF008F96A6 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = "<group>"; };
|
||||
375DFB5726F9DA010013F468 /* InstancesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstancesModel.swift; sourceTree = "<group>"; };
|
||||
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
|
||||
@ -490,7 +488,6 @@
|
||||
37BA794626DC2E56002A0235 /* AppSidebarSubscriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarSubscriptions.swift; sourceTree = "<group>"; };
|
||||
37BA794A26DC30EC002A0235 /* AppSidebarPlaylists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarPlaylists.swift; sourceTree = "<group>"; };
|
||||
37BA794E26DC3E0E002A0235 /* Int+Format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Format.swift"; sourceTree = "<group>"; };
|
||||
37BA796326DC40CB002A0235 /* Shared Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Shared Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
37BD07B42698AA4D003EBB87 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
@ -508,6 +505,8 @@
|
||||
37C3A24C272360470087A57A /* ChannelPlaylist+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChannelPlaylist+Fixtures.swift"; sourceTree = "<group>"; };
|
||||
37C3A250272366440087A57A /* ChannelPlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelPlaylistView.swift; sourceTree = "<group>"; };
|
||||
37C7A1D4267BFD9D0010EAD6 /* SponsorBlockSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorBlockSegment.swift; sourceTree = "<group>"; };
|
||||
37CB12782724C76D00213B45 /* VideoURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoURLParser.swift; sourceTree = "<group>"; };
|
||||
37CB127B2724C79D00213B45 /* VideoURLParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoURLParserTests.swift; sourceTree = "<group>"; };
|
||||
37CC3F44270CE30600608308 /* PlayerQueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueItem.swift; sourceTree = "<group>"; };
|
||||
37CC3F4B270CFE1700608308 /* PlayerQueueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueView.swift; sourceTree = "<group>"; };
|
||||
37CC3F4F270D010D00608308 /* VideoBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoBanner.swift; sourceTree = "<group>"; };
|
||||
@ -548,13 +547,6 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
37BA796026DC40CB002A0235 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C62671614900C925CA /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -819,14 +811,6 @@
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BA796426DC40CB002A0235 /* Shared Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BA796C26DC4105002A0235 /* Extensions */,
|
||||
);
|
||||
path = "Shared Tests";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
37BA796C26DC4105002A0235 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -839,8 +823,10 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37FD43E1270472060073EE42 /* Settings */,
|
||||
374C0542272496E4009BDDBE /* AppDelegate.swift */,
|
||||
37BE0BDB26A2367F0092E2DB /* Player.swift */,
|
||||
37BE0BD926A214630092E2DB /* PlayerViewController.swift */,
|
||||
374C0544272496FD009BDDBE /* Info.plist */,
|
||||
);
|
||||
path = macOS;
|
||||
sourceTree = "<group>";
|
||||
@ -868,7 +854,6 @@
|
||||
37D4B1B72672CFE300C925CA /* Model */,
|
||||
37C7A9022679058300E721B4 /* Extensions */,
|
||||
3748186426A762300084E870 /* Fixtures */,
|
||||
37BA796426DC40CB002A0235 /* Shared Tests */,
|
||||
377FC7D1267A080300A6BBAF /* Frameworks */,
|
||||
37D4B0CA2671614900C925CA /* Products */,
|
||||
37D4B174267164B000C925CA /* Tests Apple TV */,
|
||||
@ -895,6 +880,7 @@
|
||||
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */,
|
||||
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
||||
3700155E271B12DD0049C794 /* SiestaConfiguration.swift */,
|
||||
37CB12782724C76D00213B45 /* VideoURLParser.swift */,
|
||||
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
||||
37BD07C42698ADEE003EBB87 /* Pearvidious.entitlements */,
|
||||
);
|
||||
@ -910,7 +896,6 @@
|
||||
37D4B0DE2671614900C925CA /* Tests (macOS).xctest */,
|
||||
37D4B158267164AE00C925CA /* Pearvidious (tvOS).app */,
|
||||
37D4B171267164B000C925CA /* Tests (tvOS).xctest */,
|
||||
37BA796326DC40CB002A0235 /* Shared Tests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -926,7 +911,9 @@
|
||||
37D4B0E12671614900C925CA /* Tests macOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37BA796C26DC4105002A0235 /* Extensions */,
|
||||
37D4B0E22671614900C925CA /* Tests_macOS.swift */,
|
||||
37CB127B2724C79D00213B45 /* VideoURLParserTests.swift */,
|
||||
);
|
||||
path = "Tests macOS";
|
||||
sourceTree = "<group>";
|
||||
@ -1007,24 +994,6 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
37BA796226DC40CB002A0235 /* Shared Tests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 37BA796926DC40CB002A0235 /* Build configuration list for PBXNativeTarget "Shared Tests" */;
|
||||
buildPhases = (
|
||||
37BA795F26DC40CB002A0235 /* Sources */,
|
||||
37BA796026DC40CB002A0235 /* Frameworks */,
|
||||
37BA796126DC40CB002A0235 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
37BA796826DC40CB002A0235 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Shared Tests";
|
||||
productName = "Shared Tests";
|
||||
productReference = 37BA796326DC40CB002A0235 /* Shared Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
37D4B0C82671614900C925CA /* Pearvidious (iOS) */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */;
|
||||
@ -1177,10 +1146,6 @@
|
||||
LastSwiftUpdateCheck = 1300;
|
||||
LastUpgradeCheck = 1310;
|
||||
TargetAttributes = {
|
||||
37BA796226DC40CB002A0235 = {
|
||||
CreatedOnToolsVersion = 13.0;
|
||||
TestTargetID = 37D4B0CE2671614900C925CA;
|
||||
};
|
||||
37D4B0C82671614900C925CA = {
|
||||
CreatedOnToolsVersion = 13.0;
|
||||
};
|
||||
@ -1241,7 +1206,6 @@
|
||||
37D4B0D32671614900C925CA /* Tests (iOS) */,
|
||||
37D4B0DD2671614900C925CA /* Tests (macOS) */,
|
||||
37D4B170267164B000C925CA /* Tests (tvOS) */,
|
||||
37BA796226DC40CB002A0235 /* Shared Tests */,
|
||||
37FD43E62704A2240073EE42 /* Periphery (macOS) */,
|
||||
37FD43EB2704A7710073EE42 /* Periphery (tvOS) */,
|
||||
);
|
||||
@ -1249,13 +1213,6 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
37BA796126DC40CB002A0235 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C72671614900C925CA /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -1393,15 +1350,6 @@
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
37BA795F26DC40CB002A0235 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
37BA797026DC426B002A0235 /* Int+Format.swift in Sources */,
|
||||
37BA796F26DC412E002A0235 /* Int+FormatTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
37D4B0C52671614900C925CA /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -1409,6 +1357,7 @@
|
||||
37CEE4BD2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||
37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */,
|
||||
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
|
||||
37CB12792724C76D00213B45 /* VideoURLParser.swift in Sources */,
|
||||
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
|
||||
3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */,
|
||||
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */,
|
||||
@ -1566,6 +1515,7 @@
|
||||
3765788A2685471400D4EA09 /* Playlist.swift in Sources */,
|
||||
37E2EEAC270656EC00170416 /* PlayerControlsView.swift in Sources */,
|
||||
37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
||||
374C0543272496E4009BDDBE /* AppDelegate.swift in Sources */,
|
||||
373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */,
|
||||
37AAF29126740715007FC770 /* Channel.swift in Sources */,
|
||||
376A33E12720CAD6000C1D6B /* VideosApp.swift in Sources */,
|
||||
@ -1626,6 +1576,7 @@
|
||||
37C3A24A27235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */,
|
||||
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */,
|
||||
3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */,
|
||||
37CB127A2724C76D00213B45 /* VideoURLParser.swift in Sources */,
|
||||
37BA794426DBA973002A0235 /* PlaylistsModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1642,6 +1593,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
37CB128B2724CC1F00213B45 /* VideoURLParserTests.swift in Sources */,
|
||||
37CB128C2724CC8400213B45 /* VideoURLParser.swift in Sources */,
|
||||
37D4B0E32671614900C925CA /* Tests_macOS.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1762,11 +1715,6 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
37BA796826DC40CB002A0235 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 37D4B0CE2671614900C925CA /* Pearvidious (macOS) */;
|
||||
targetProxy = 37BA796726DC40CB002A0235 /* PBXContainerItemProxy */;
|
||||
};
|
||||
37D4B0D62671614900C925CA /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 37D4B0C82671614900C925CA /* Pearvidious (iOS) */;
|
||||
@ -1785,58 +1733,6 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
37BA796A26DC40CB002A0235 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.arekf.Shared-Tests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pearvidious.app/Contents/MacOS/Pearvidious";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
37BA796B26DC40CB002A0235 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "net.arekf.Shared-Tests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Pearvidious.app/Contents/MacOS/Pearvidious";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
37D4B0EA2671614900C925CA /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -2027,6 +1923,7 @@
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = macOS/Info.plist;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2059,6 +1956,7 @@
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SELECTED_FILES = readonly;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = macOS/Info.plist;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -2333,15 +2231,6 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
37BA796926DC40CB002A0235 /* Build configuration list for PBXNativeTarget "Shared Tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
37BA796A26DC40CB002A0235 /* Debug */,
|
||||
37BA796B26DC40CB002A0235 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
37D4B0C02671614700C925CA /* Build configuration list for PBXProject "Pearvidious" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -38,16 +38,6 @@
|
||||
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "37BA796226DC40CB002A0235"
|
||||
BuildableName = "Shared Tests.xctest"
|
||||
BlueprintName = "Shared Tests"
|
||||
ReferencedContainer = "container:Pearvidious.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
|
@ -1,8 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Pearvidious
|
||||
//
|
||||
// Created by Arkadiusz Fal on 20/10/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
@ -4,16 +4,20 @@ import Foundation
|
||||
extension Defaults.Keys {
|
||||
static let invidiousInstanceID = "default-invidious-instance"
|
||||
static let pipedInstanceID = "default-piped-instance"
|
||||
static let privateAccountID = "default-private-invidious-account"
|
||||
|
||||
static let instances = Key<[Instance]>("instances", default: [
|
||||
.init(app: .piped, id: pipedInstanceID, name: "Public", url: "https://pipedapi.kavin.rocks"),
|
||||
.init(app: .invidious, id: invidiousInstanceID, name: "Private", url: "https://invidious.home.arekf.net")
|
||||
])
|
||||
static let accounts = Key<[Account]>("accounts", default: [
|
||||
.init(instanceID: invidiousInstanceID,
|
||||
name: "arekf",
|
||||
url: "https://invidious.home.arekf.net",
|
||||
sid: "ki55SJbaQmm0bOxUWctGAQLYPQRgk-CXDPw5Dp4oBmI=")
|
||||
.init(
|
||||
id: privateAccountID,
|
||||
instanceID: invidiousInstanceID,
|
||||
name: "arekf",
|
||||
url: "https://invidious.home.arekf.net",
|
||||
sid: "ki55SJbaQmm0bOxUWctGAQLYPQRgk-CXDPw5Dp4oBmI="
|
||||
)
|
||||
])
|
||||
static let lastAccountID = Key<Account.ID?>("lastAccountID")
|
||||
static let lastInstanceID = Key<Instance.ID?>("lastInstanceID")
|
||||
|
@ -34,6 +34,10 @@ struct ContentView: View {
|
||||
#endif
|
||||
}
|
||||
.onAppear(perform: configure)
|
||||
|
||||
.handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"]))
|
||||
.onOpenURL(perform: handleOpenedURL)
|
||||
|
||||
.environmentObject(accounts)
|
||||
.environmentObject(instances)
|
||||
.environmentObject(navigation)
|
||||
@ -113,6 +117,25 @@ struct ContentView: View {
|
||||
|
||||
navigation.presentingWelcomeScreen = true
|
||||
}
|
||||
|
||||
func handleOpenedURL(_ url: URL) {
|
||||
guard !accounts.current.isNil else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = VideoURLParser(url: url)
|
||||
|
||||
guard let id = parser.id else {
|
||||
return
|
||||
}
|
||||
|
||||
accounts.api.video(id).load().onSuccess { response in
|
||||
if let video: Video = response.typedContent() {
|
||||
self.player.playNow(video, at: parser.time)
|
||||
self.player.presentPlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
|
@ -3,10 +3,15 @@ import SwiftUI
|
||||
|
||||
@main
|
||||
struct PearvidiousApp: App {
|
||||
#if os(macOS)
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
#endif
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
.handlesExternalEvents(matching: Set(["*"]))
|
||||
#if !os(tvOS)
|
||||
.commands {
|
||||
SidebarCommands()
|
||||
|
78
Shared/VideoURLParser.swift
Normal file
78
Shared/VideoURLParser.swift
Normal file
@ -0,0 +1,78 @@
|
||||
import Foundation
|
||||
|
||||
struct VideoURLParser {
|
||||
let url: URL
|
||||
|
||||
var id: String? {
|
||||
if urlComponents?.host == "youtu.be", let path = urlComponents?.path {
|
||||
return String(path.suffix(from: path.index(path.startIndex, offsetBy: 1)))
|
||||
}
|
||||
|
||||
return queryItemValue("v")
|
||||
}
|
||||
|
||||
var time: TimeInterval? {
|
||||
guard let time = queryItemValue("t") else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let timeComponents = parseTime(time)
|
||||
|
||||
guard !timeComponents.isEmpty,
|
||||
let hours = TimeInterval(timeComponents["hours"] ?? "0"),
|
||||
let minutes = TimeInterval(timeComponents["minutes"] ?? "0"),
|
||||
let seconds = TimeInterval(timeComponents["seconds"] ?? "0")
|
||||
else {
|
||||
if let time = TimeInterval(time) {
|
||||
return time
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return seconds + (minutes * 60) + (hours * 60 * 60)
|
||||
}
|
||||
|
||||
func queryItemValue(_ name: String) -> String? {
|
||||
queryItems.first { $0.name == name }?.value
|
||||
}
|
||||
|
||||
private var queryItems: [URLQueryItem] {
|
||||
urlComponents?.queryItems ?? []
|
||||
}
|
||||
|
||||
private var urlComponents: URLComponents? {
|
||||
URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||
}
|
||||
|
||||
private func parseTime(_ time: String) -> [String: String] {
|
||||
let results = timeRegularExpression.matches(
|
||||
in: time,
|
||||
range: NSRange(time.startIndex..., in: time)
|
||||
)
|
||||
|
||||
guard let match = results.first else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
var components: [String: String] = [:]
|
||||
|
||||
for name in ["hours", "minutes", "seconds"] {
|
||||
let matchRange = match.range(withName: name)
|
||||
|
||||
if let substringRange = Range(matchRange, in: time) {
|
||||
let capture = String(time[substringRange])
|
||||
components[name] = capture
|
||||
}
|
||||
}
|
||||
|
||||
return components
|
||||
}
|
||||
|
||||
private var timeRegularExpression: NSRegularExpression {
|
||||
try! NSRegularExpression(
|
||||
pattern: "(?:(?<hours>[0-9+])+h)?(?:(?<minutes>[0-9]+)m)?(?:(?<seconds>[0-9]*)s)?",
|
||||
options: .caseInsensitive
|
||||
)
|
||||
}
|
||||
}
|
@ -31,7 +31,6 @@ struct HorizontalCells: View {
|
||||
.padding(.vertical, 10)
|
||||
#endif
|
||||
}
|
||||
.id(items.map(\.id).joined())
|
||||
#if os(tvOS)
|
||||
.frame(height: 560)
|
||||
#else
|
||||
|
@ -17,7 +17,6 @@ struct VerticalCells: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.id(items.map(\.id).joined())
|
||||
.edgesIgnoringSafeArea(.horizontal)
|
||||
#if os(macOS)
|
||||
.background()
|
||||
|
@ -8,28 +8,6 @@
|
||||
import XCTest
|
||||
|
||||
class Tests_macOS: XCTestCase {
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use recording to get started writing UI tests.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testLaunchPerformance() throws {
|
||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// This measures how long it takes to launch your application.
|
||||
|
39
Tests macOS/VideoURLParserTests.swift
Normal file
39
Tests macOS/VideoURLParserTests.swift
Normal file
@ -0,0 +1,39 @@
|
||||
import XCTest
|
||||
|
||||
final class VideoURLParserTests: XCTestCase {
|
||||
func testIDParsing() throws {
|
||||
let samples: [String: String] = [
|
||||
"https://www.youtube.com/watch?v=_E0PWQvW-14&list=WL&index=4&t=155s": "_E0PWQvW-14",
|
||||
"https://youtu.be/IRsc57nK8mg?t=20": "IRsc57nK8mg",
|
||||
"https://www.youtube-nocookie.com/watch?index=4&v=cE1PSQrWc11&list=WL&t=155s": "cE1PSQrWc11",
|
||||
"https://invidious.snopyta.org/watch?v=XpowfENlJAw" : "XpowfENlJAw",
|
||||
"/watch?v=VQ_f5RymW70" : "VQ_f5RymW70",
|
||||
"watch?v=IUTGFQpKaPU&t=30s": "IUTGFQpKaPU"
|
||||
]
|
||||
|
||||
samples.forEach { url, id in
|
||||
XCTAssertEqual(
|
||||
VideoURLParser(url: URL(string: url)!).id,
|
||||
id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testTimeParsing() throws {
|
||||
let samples: [String: TimeInterval?] = [
|
||||
"https://www.youtube.com/watch?v=_E0PWQvW-14&list=WL&index=4&t=155s": 155,
|
||||
"https://youtu.be/IRsc57nK8mg?t=20m10s": 1210,
|
||||
"https://youtu.be/IRsc57nK8mg?t=3x4z": nil,
|
||||
"https://www.youtube-nocookie.com/watch?index=4&v=cE1PSQrWc11&list=WL&t=2H3m5s": 7385,
|
||||
"https://youtu.be/VQ_f5RymW70?t=378": 378,
|
||||
"watch?v=IUTGFQpKaPU&t=30s": 30
|
||||
]
|
||||
|
||||
samples.forEach { url, time in
|
||||
XCTAssertEqual(
|
||||
VideoURLParser(url: URL(string: url)!).time,
|
||||
time
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,19 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>net.yattee.app.watch</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>yattee</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
|
8
macOS/AppDelegate.swift
Normal file
8
macOS/AppDelegate.swift
Normal file
@ -0,0 +1,8 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
|
||||
true
|
||||
}
|
||||
}
|
19
macOS/Info.plist
Normal file
19
macOS/Info.plist
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>net.yattee.app.watch</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>yattee</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
Loading…
Reference in New Issue
Block a user