mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Popular videos, playing from mp4
This commit is contained in:
parent
d488ee6679
commit
1772728cb8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# SwiftLint Remote Config Cache
|
||||||
|
.swiftlint/RemoteConfigCache
|
14
.swiftlint.yml
Normal file
14
.swiftlint.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
parent_config: https://raw.githubusercontent.com/sindresorhus/swiftlint-config/main/.swiftlint.yml
|
||||||
|
|
||||||
|
excluded:
|
||||||
|
- .build
|
||||||
|
|
||||||
|
identifier_name:
|
||||||
|
excluded:
|
||||||
|
- db
|
||||||
|
- id
|
||||||
|
- vm
|
||||||
|
|
||||||
|
type_name:
|
||||||
|
excluded:
|
||||||
|
- VM
|
8
Apple TV/Info.plist
Normal file
8
Apple TV/Info.plist
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?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>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
14
Model/DataProvider.swift
Normal file
14
Model/DataProvider.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class DataProvider: ObservableObject {
|
||||||
|
static let instance = "https://invidious.home.arekf.net"
|
||||||
|
|
||||||
|
static func request(_ path: String) -> DataRequest {
|
||||||
|
return AF.request(apiURLString(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
static func apiURLString(_ path: String) -> String {
|
||||||
|
"\(instance)/api/v1/\(path)"
|
||||||
|
}
|
||||||
|
}
|
18
Model/PopularVideosProvider.swift
Normal file
18
Model/PopularVideosProvider.swift
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
final class PopluarVideosProvider: DataProvider {
|
||||||
|
@Published var videos = [Video]()
|
||||||
|
|
||||||
|
func load() {
|
||||||
|
DataProvider.request("popular").responseJSON { response in
|
||||||
|
switch response.result {
|
||||||
|
case let .success(value):
|
||||||
|
self.videos = JSON(value).arrayValue.map { Video($0) }
|
||||||
|
case let .failure(error):
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Model/Video.swift
Normal file
39
Model/Video.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
class Video: Identifiable, ObservableObject {
|
||||||
|
let id: String
|
||||||
|
var title: String
|
||||||
|
var thumbnailURL: URL
|
||||||
|
var author: String
|
||||||
|
|
||||||
|
@Published var url: URL?
|
||||||
|
@Published var error: Bool = false
|
||||||
|
|
||||||
|
init(id: String, title: String, thumbnailURL: URL, author: String) {
|
||||||
|
self.id = id
|
||||||
|
self.title = title
|
||||||
|
self.thumbnailURL = thumbnailURL
|
||||||
|
self.author = author
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ json: JSON) {
|
||||||
|
id = json["videoId"].stringValue
|
||||||
|
title = json["title"].stringValue
|
||||||
|
thumbnailURL = json["videoThumbnails"][0]["url"].url!
|
||||||
|
author = json["author"].stringValue
|
||||||
|
url = formatStreamURL(from: json["formatStreams"].arrayValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatStreamURL(from streams: [JSON]) -> URL? {
|
||||||
|
if streams.isEmpty {
|
||||||
|
error = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = streams.last!
|
||||||
|
|
||||||
|
return stream["url"].url
|
||||||
|
}
|
||||||
|
}
|
25
Model/VideoDetailsProvider.swift
Normal file
25
Model/VideoDetailsProvider.swift
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import Alamofire
|
||||||
|
import Foundation
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
|
final class VideoDetailsProvider: DataProvider {
|
||||||
|
@Published var video: Video?
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
|
||||||
|
init(_ id: String) {
|
||||||
|
self.id = id
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func load() {
|
||||||
|
DataProvider.request("videos/\(id)").responseJSON { response in
|
||||||
|
switch response.result {
|
||||||
|
case let .success(value):
|
||||||
|
self.video = Video(JSON(value))
|
||||||
|
case let .failure(error):
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,29 @@
|
|||||||
37D4B176267164B000C925CA /* PearvidiousUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B175267164B000C925CA /* PearvidiousUITests.swift */; };
|
37D4B176267164B000C925CA /* PearvidiousUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B175267164B000C925CA /* PearvidiousUITests.swift */; };
|
||||||
37D4B1802671650A00C925CA /* PearvidiousApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C22671614700C925CA /* PearvidiousApp.swift */; };
|
37D4B1802671650A00C925CA /* PearvidiousApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C22671614700C925CA /* PearvidiousApp.swift */; };
|
||||||
37D4B1812671653A00C925CA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C32671614700C925CA /* ContentView.swift */; };
|
37D4B1812671653A00C925CA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B0C32671614700C925CA /* ContentView.swift */; };
|
||||||
|
37D4B1832671681B00C925CA /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1822671681B00C925CA /* PlayerView.swift */; };
|
||||||
|
37D4B1842671684E00C925CA /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1822671681B00C925CA /* PlayerView.swift */; };
|
||||||
|
37D4B1852671684F00C925CA /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1822671681B00C925CA /* PlayerView.swift */; };
|
||||||
|
37D4B1862671691600C925CA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37D4B0C42671614800C925CA /* Assets.xcassets */; };
|
||||||
|
37D4B18C26717B3800C925CA /* VideoThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoThumbnailView.swift */; };
|
||||||
|
37D4B18D26717B3800C925CA /* VideoThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoThumbnailView.swift */; };
|
||||||
|
37D4B18E26717B3800C925CA /* VideoThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B18B26717B3800C925CA /* VideoThumbnailView.swift */; };
|
||||||
|
37D4B19126717C6900C925CA /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 37D4B19026717C6900C925CA /* Alamofire */; };
|
||||||
|
37D4B19326717CE100C925CA /* PopularVideosProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19226717CE100C925CA /* PopularVideosProvider.swift */; };
|
||||||
|
37D4B19426717CE100C925CA /* PopularVideosProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19226717CE100C925CA /* PopularVideosProvider.swift */; };
|
||||||
|
37D4B19526717CE100C925CA /* PopularVideosProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19226717CE100C925CA /* PopularVideosProvider.swift */; };
|
||||||
|
37D4B19726717E1500C925CA /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19626717E1500C925CA /* Video.swift */; };
|
||||||
|
37D4B19826717E1500C925CA /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19626717E1500C925CA /* Video.swift */; };
|
||||||
|
37D4B19926717E1500C925CA /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B19626717E1500C925CA /* Video.swift */; };
|
||||||
|
37D4B19D2671817900C925CA /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 37D4B19C2671817900C925CA /* SwiftyJSON */; };
|
||||||
|
37D4B1AB2672580400C925CA /* URLImage in Frameworks */ = {isa = PBXBuildFile; productRef = 37D4B1AA2672580400C925CA /* URLImage */; };
|
||||||
|
37D4B1AD2672580400C925CA /* URLImageStore in Frameworks */ = {isa = PBXBuildFile; productRef = 37D4B1AC2672580400C925CA /* URLImageStore */; };
|
||||||
|
37D4B1B02672A01000C925CA /* DataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1AF2672A01000C925CA /* DataProvider.swift */; };
|
||||||
|
37D4B1B12672A01000C925CA /* DataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1AF2672A01000C925CA /* DataProvider.swift */; };
|
||||||
|
37D4B1B22672A01000C925CA /* DataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1AF2672A01000C925CA /* DataProvider.swift */; };
|
||||||
|
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
|
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
|
37D4B1B62672A30700C925CA /* VideoDetailsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -59,6 +82,13 @@
|
|||||||
37D4B15E267164AF00C925CA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
37D4B15E267164AF00C925CA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
37D4B171267164B000C925CA /* Tests Apple TV.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests Apple TV.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
37D4B171267164B000C925CA /* Tests Apple TV.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests Apple TV.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
37D4B175267164B000C925CA /* PearvidiousUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearvidiousUITests.swift; sourceTree = "<group>"; };
|
37D4B175267164B000C925CA /* PearvidiousUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearvidiousUITests.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B1822671681B00C925CA /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B18B26717B3800C925CA /* VideoThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoThumbnailView.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B19226717CE100C925CA /* PopularVideosProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularVideosProvider.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B19626717E1500C925CA /* Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B1AE26729DEB00C925CA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
37D4B1AF2672A01000C925CA /* DataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProvider.swift; sourceTree = "<group>"; };
|
||||||
|
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoDetailsProvider.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -94,6 +124,10 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37D4B1AD2672580400C925CA /* URLImageStore in Frameworks */,
|
||||||
|
37D4B19D2671817900C925CA /* SwiftyJSON in Frameworks */,
|
||||||
|
37D4B1AB2672580400C925CA /* URLImage in Frameworks */,
|
||||||
|
37D4B19126717C6900C925CA /* Alamofire in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -110,6 +144,7 @@
|
|||||||
37D4B0BC2671614700C925CA = {
|
37D4B0BC2671614700C925CA = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
37D4B1B72672CFE300C925CA /* Model */,
|
||||||
37D4B0C12671614700C925CA /* Shared */,
|
37D4B0C12671614700C925CA /* Shared */,
|
||||||
37D4B159267164AE00C925CA /* Apple TV */,
|
37D4B159267164AE00C925CA /* Apple TV */,
|
||||||
37D4B174267164B000C925CA /* Tests Apple TV */,
|
37D4B174267164B000C925CA /* Tests Apple TV */,
|
||||||
@ -122,8 +157,10 @@
|
|||||||
37D4B0C12671614700C925CA /* Shared */ = {
|
37D4B0C12671614700C925CA /* Shared */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
|
||||||
37D4B0C32671614700C925CA /* ContentView.swift */,
|
37D4B0C32671614700C925CA /* ContentView.swift */,
|
||||||
|
37D4B18B26717B3800C925CA /* VideoThumbnailView.swift */,
|
||||||
|
37D4B1822671681B00C925CA /* PlayerView.swift */,
|
||||||
|
37D4B0C22671614700C925CA /* PearvidiousApp.swift */,
|
||||||
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
37D4B0C42671614800C925CA /* Assets.xcassets */,
|
||||||
);
|
);
|
||||||
path = Shared;
|
path = Shared;
|
||||||
@ -161,6 +198,7 @@
|
|||||||
37D4B159267164AE00C925CA /* Apple TV */ = {
|
37D4B159267164AE00C925CA /* Apple TV */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
37D4B1AE26729DEB00C925CA /* Info.plist */,
|
||||||
37D4B15E267164AF00C925CA /* Assets.xcassets */,
|
37D4B15E267164AF00C925CA /* Assets.xcassets */,
|
||||||
);
|
);
|
||||||
path = "Apple TV";
|
path = "Apple TV";
|
||||||
@ -174,6 +212,17 @@
|
|||||||
path = "Tests Apple TV";
|
path = "Tests Apple TV";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
37D4B1B72672CFE300C925CA /* Model */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
37D4B19626717E1500C925CA /* Video.swift */,
|
||||||
|
37D4B19226717CE100C925CA /* PopularVideosProvider.swift */,
|
||||||
|
37D4B1B32672A30700C925CA /* VideoDetailsProvider.swift */,
|
||||||
|
37D4B1AF2672A01000C925CA /* DataProvider.swift */,
|
||||||
|
);
|
||||||
|
path = Model;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -260,6 +309,12 @@
|
|||||||
dependencies = (
|
dependencies = (
|
||||||
);
|
);
|
||||||
name = "Pearvidious (Apple TV)";
|
name = "Pearvidious (Apple TV)";
|
||||||
|
packageProductDependencies = (
|
||||||
|
37D4B19026717C6900C925CA /* Alamofire */,
|
||||||
|
37D4B19C2671817900C925CA /* SwiftyJSON */,
|
||||||
|
37D4B1AA2672580400C925CA /* URLImage */,
|
||||||
|
37D4B1AC2672580400C925CA /* URLImageStore */,
|
||||||
|
);
|
||||||
productName = Pearvidious;
|
productName = Pearvidious;
|
||||||
productReference = 37D4B158267164AE00C925CA /* Pearvidious (Apple TV).app */;
|
productReference = 37D4B158267164AE00C925CA /* Pearvidious (Apple TV).app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
@ -324,6 +379,11 @@
|
|||||||
Base,
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = 37D4B0BC2671614700C925CA;
|
mainGroup = 37D4B0BC2671614700C925CA;
|
||||||
|
packageReferences = (
|
||||||
|
37D4B18F26717C6900C925CA /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||||
|
37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||||
|
37D4B1A92672580400C925CA /* XCRemoteSwiftPackageReference "url-image" */,
|
||||||
|
);
|
||||||
productRefGroup = 37D4B0CA2671614900C925CA /* Products */;
|
productRefGroup = 37D4B0CA2671614900C925CA /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@ -373,6 +433,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37D4B1862671691600C925CA /* Assets.xcassets in Resources */,
|
||||||
37D4B15F267164AF00C925CA /* Assets.xcassets in Resources */,
|
37D4B15F267164AF00C925CA /* Assets.xcassets in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -391,7 +452,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37D4B19326717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
||||||
|
37D4B1832671681B00C925CA /* PlayerView.swift in Sources */,
|
||||||
37D4B0E62671614900C925CA /* ContentView.swift in Sources */,
|
37D4B0E62671614900C925CA /* ContentView.swift in Sources */,
|
||||||
|
37D4B1B02672A01000C925CA /* DataProvider.swift in Sources */,
|
||||||
|
37D4B18C26717B3800C925CA /* VideoThumbnailView.swift in Sources */,
|
||||||
|
37D4B1B42672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
||||||
|
37D4B19726717E1500C925CA /* Video.swift in Sources */,
|
||||||
37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */,
|
37D4B0E42671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -400,7 +467,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37D4B19426717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
||||||
|
37D4B1852671684F00C925CA /* PlayerView.swift in Sources */,
|
||||||
37D4B0E72671614900C925CA /* ContentView.swift in Sources */,
|
37D4B0E72671614900C925CA /* ContentView.swift in Sources */,
|
||||||
|
37D4B1B12672A01000C925CA /* DataProvider.swift in Sources */,
|
||||||
|
37D4B18D26717B3800C925CA /* VideoThumbnailView.swift in Sources */,
|
||||||
|
37D4B1B52672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
||||||
|
37D4B19826717E1500C925CA /* Video.swift in Sources */,
|
||||||
37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */,
|
37D4B0E52671614900C925CA /* PearvidiousApp.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -425,7 +498,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
37D4B19526717CE100C925CA /* PopularVideosProvider.swift in Sources */,
|
||||||
|
37D4B1842671684E00C925CA /* PlayerView.swift in Sources */,
|
||||||
37D4B1802671650A00C925CA /* PearvidiousApp.swift in Sources */,
|
37D4B1802671650A00C925CA /* PearvidiousApp.swift in Sources */,
|
||||||
|
37D4B1B22672A01000C925CA /* DataProvider.swift in Sources */,
|
||||||
|
37D4B18E26717B3800C925CA /* VideoThumbnailView.swift in Sources */,
|
||||||
|
37D4B1B62672A30700C925CA /* VideoDetailsProvider.swift in Sources */,
|
||||||
|
37D4B19926717E1500C925CA /* Video.swift in Sources */,
|
||||||
37D4B1812671653A00C925CA /* ContentView.swift in Sources */,
|
37D4B1812671653A00C925CA /* ContentView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -801,6 +880,11 @@
|
|||||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Apple TV/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Pearvidious;
|
||||||
|
INFOPLIST_KEY_CFBundleExecutable = "Pearvidious (Apple TV)";
|
||||||
|
INFOPLIST_KEY_CFBundleName = "Pearvidious (Apple TV)";
|
||||||
|
INFOPLIST_KEY_CFBundleVersion = 1;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
|
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@ -829,6 +913,11 @@
|
|||||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Apple TV/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = Pearvidious;
|
||||||
|
INFOPLIST_KEY_CFBundleExecutable = "Pearvidious (Apple TV)";
|
||||||
|
INFOPLIST_KEY_CFBundleName = "Pearvidious (Apple TV)";
|
||||||
|
INFOPLIST_KEY_CFBundleVersion = 1;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
|
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@ -965,6 +1054,56 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
37D4B18F26717C6900C925CA /* XCRemoteSwiftPackageReference "Alamofire" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/Alamofire/Alamofire.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
37D4B1A92672580400C925CA /* XCRemoteSwiftPackageReference "url-image" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/dmytro-anokhin/url-image";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 3.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
37D4B19026717C6900C925CA /* Alamofire */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37D4B18F26717C6900C925CA /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
|
productName = Alamofire;
|
||||||
|
};
|
||||||
|
37D4B19C2671817900C925CA /* SwiftyJSON */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37D4B19B2671817900C925CA /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||||
|
productName = SwiftyJSON;
|
||||||
|
};
|
||||||
|
37D4B1AA2672580400C925CA /* URLImage */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37D4B1A92672580400C925CA /* XCRemoteSwiftPackageReference "url-image" */;
|
||||||
|
productName = URLImage;
|
||||||
|
};
|
||||||
|
37D4B1AC2672580400C925CA /* URLImageStore */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 37D4B1A92672580400C925CA /* XCRemoteSwiftPackageReference "url-image" */;
|
||||||
|
productName = URLImageStore;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 37D4B0BD2671614700C925CA /* Project object */;
|
rootObject = 37D4B0BD2671614700C925CA /* Project object */;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"object": {
|
||||||
|
"pins": [
|
||||||
|
{
|
||||||
|
"package": "Alamofire",
|
||||||
|
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "f96b619bcb2383b43d898402283924b80e2c4bae",
|
||||||
|
"version": "5.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "SwiftyJSON",
|
||||||
|
"repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07",
|
||||||
|
"version": "5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "url-image",
|
||||||
|
"repositoryURL": "https://github.com/dmytro-anokhin/url-image",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "a55b6597f6ce67dfbdc136ecfb8c8436b14ca41d",
|
||||||
|
"version": "3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"version": 1
|
||||||
|
}
|
@ -19,6 +19,27 @@
|
|||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>Playground (Playground) 1.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
</dict>
|
||||||
|
<key>Playground (Playground) 2.xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>5</integer>
|
||||||
|
</dict>
|
||||||
|
<key>Playground (Playground).xcscheme</key>
|
||||||
|
<dict>
|
||||||
|
<key>isShown</key>
|
||||||
|
<false/>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SuppressBuildableAutocreation</key>
|
<key>SuppressBuildableAutocreation</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -1,16 +1,32 @@
|
|||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// Shared
|
|
||||||
//
|
|
||||||
// Created by Arkadiusz Fal on 09/06/2021.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
@ObservedObject var popular = PopluarVideosProvider()
|
||||||
|
|
||||||
|
var items: [GridItem] {
|
||||||
|
Array(repeating: .init(.flexible()), count: 4)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text("Hello, world!")
|
NavigationView {
|
||||||
.padding()
|
TabView {
|
||||||
|
Group {
|
||||||
|
List {
|
||||||
|
ForEach(popular.videos) { video in
|
||||||
|
VideoThumbnailView(video: video)
|
||||||
|
.listRowInsets(EdgeInsets(top: .zero, leading: .zero, bottom: .zero, trailing: 30))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listStyle(GroupedListStyle())
|
||||||
|
}
|
||||||
|
.tabItem { Text("Popular") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.task {
|
||||||
|
async {
|
||||||
|
popular.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
//
|
|
||||||
// PearvidiousApp.swift
|
|
||||||
// Shared
|
|
||||||
//
|
|
||||||
// Created by Arkadiusz Fal on 09/06/2021.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import URLImage
|
||||||
|
import URLImageStore
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct PearvidiousApp: App {
|
struct PearvidiousApp: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
let urlImageService = URLImageService(fileStore: URLImageFileStore(),
|
||||||
|
inMemoryStore: URLImageInMemoryStore())
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView()
|
ContentView()
|
||||||
|
.environment(\.urlImageService, urlImageService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
Shared/PlayerView.swift
Normal file
61
Shared/PlayerView.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import AVKit
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PlayerView: View {
|
||||||
|
@ObservedObject var provider: VideoDetailsProvider
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
if let video = provider.video {
|
||||||
|
if video.url != nil {
|
||||||
|
PlayerViewController(video)
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
}
|
||||||
|
|
||||||
|
if video.error {
|
||||||
|
Text("Video can not be loaded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.task {
|
||||||
|
async {
|
||||||
|
provider.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PlayerViewController: UIViewControllerRepresentable {
|
||||||
|
var video: Video
|
||||||
|
|
||||||
|
init(_ video: Video) {
|
||||||
|
self.video = video
|
||||||
|
}
|
||||||
|
|
||||||
|
private var player: AVPlayer {
|
||||||
|
let item = AVPlayerItem(url: video.url!)
|
||||||
|
item.externalMetadata = [makeMetadataItem(.commonIdentifierTitle, value: video.title)]
|
||||||
|
|
||||||
|
return AVPlayer(playerItem: item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeMetadataItem(_ identifier: AVMetadataIdentifier, value: Any) -> AVMetadataItem {
|
||||||
|
let item = AVMutableMetadataItem()
|
||||||
|
item.identifier = identifier
|
||||||
|
item.value = value as? NSCopying & NSObjectProtocol
|
||||||
|
item.extendedLanguageTag = "und"
|
||||||
|
return item.copy() as! AVMetadataItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUIViewController(context _: Context) -> AVPlayerViewController {
|
||||||
|
let controller = AVPlayerViewController()
|
||||||
|
controller.modalPresentationStyle = .fullScreen
|
||||||
|
controller.player = player
|
||||||
|
controller.title = video.title
|
||||||
|
controller.player?.play()
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_: AVPlayerViewController, context _: Context) {}
|
||||||
|
}
|
48
Shared/VideoThumbnailView.swift
Normal file
48
Shared/VideoThumbnailView.swift
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import URLImage
|
||||||
|
import URLImageStore
|
||||||
|
|
||||||
|
struct VideoThumbnailView: View {
|
||||||
|
@Environment(\.isFocused) var focused: Bool
|
||||||
|
|
||||||
|
var video: Video
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationLink(destination: PlayerView(provider: VideoDetailsProvider(video.id))) {
|
||||||
|
HStack(alignment: .top, spacing: 2) {
|
||||||
|
// to replace with AsyncImage when it is fixed with lazy views
|
||||||
|
URLImage(video.thumbnailURL) { image in
|
||||||
|
image
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(width: 320, height: 180)
|
||||||
|
}
|
||||||
|
.mask(RoundedRectangle(cornerRadius: 12))
|
||||||
|
.frame(width: 320, height: 180)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(video.title)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.bold()
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
Text(video.author)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
}.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VideoThumbnailView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VideoThumbnailView(video: Video(
|
||||||
|
id: "A",
|
||||||
|
title: "A very very long text which",
|
||||||
|
thumbnailURL: URL(string: "https://invidious.home.arekf.net/vi/yXohcxCKqvo/maxres.jpg")!,
|
||||||
|
author: "Bear"
|
||||||
|
)).frame(maxWidth: 350)
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class PearvidiousUITests: XCTestCase {
|
class PearvidiousUITests: XCTestCase {
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class Tests_iOS: XCTestCase {
|
class Tests_iOS: XCTestCase {
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class Tests_macOS: XCTestCase {
|
class Tests_macOS: XCTestCase {
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user