mirror of
https://github.com/yattee/yattee.git
synced 2026-01-11 06:12:33 +00:00
Add experimental setting to hide videos without duration in Invidious
This adds a new instance setting for Invidious that filters out videos without duration information from feeds, popular, trending, search results, and channel pages. This can be used to hide YouTube Shorts. The setting is labeled as "Experimental: Hide videos without duration" and includes an explanation that it can be used to hide shorts. Key changes: - Added hideVideosWithoutDuration property to Instance model - Updated InstancesBridge to serialize/deserialize the new setting - Added UI toggle in InstanceSettings with explanatory footer text - Implemented filtering in InvidiousAPI for: * Popular videos * Trending videos * Search results * Subscription feed * Channel content - Videos accessed directly by URL are not filtered
This commit is contained in:
@@ -11,8 +11,9 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
|||||||
var frontendURL: String?
|
var frontendURL: String?
|
||||||
var proxiesVideos: Bool
|
var proxiesVideos: Bool
|
||||||
var invidiousCompanion: Bool
|
var invidiousCompanion: Bool
|
||||||
|
var hideVideosWithoutDuration: Bool
|
||||||
|
|
||||||
init(app: VideosApp, id: String? = nil, name: String? = nil, apiURLString: String, frontendURL: String? = nil, proxiesVideos: Bool = false, invidiousCompanion: Bool = false) {
|
init(app: VideosApp, id: String? = nil, name: String? = nil, apiURLString: String, frontendURL: String? = nil, proxiesVideos: Bool = false, invidiousCompanion: Bool = false, hideVideosWithoutDuration: Bool = false) {
|
||||||
self.app = app
|
self.app = app
|
||||||
self.id = id ?? UUID().uuidString
|
self.id = id ?? UUID().uuidString
|
||||||
self.name = name ?? app.rawValue
|
self.name = name ?? app.rawValue
|
||||||
@@ -20,6 +21,7 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
|
|||||||
self.frontendURL = frontendURL
|
self.frontendURL = frontendURL
|
||||||
self.proxiesVideos = proxiesVideos
|
self.proxiesVideos = proxiesVideos
|
||||||
self.invidiousCompanion = invidiousCompanion
|
self.invidiousCompanion = invidiousCompanion
|
||||||
|
self.hideVideosWithoutDuration = hideVideosWithoutDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiURL: URL! {
|
var apiURL: URL! {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ struct InstancesBridge: Defaults.Bridge {
|
|||||||
"apiURL": value.apiURLString,
|
"apiURL": value.apiURLString,
|
||||||
"frontendURL": value.frontendURL ?? "",
|
"frontendURL": value.frontendURL ?? "",
|
||||||
"proxiesVideos": value.proxiesVideos ? "true" : "false",
|
"proxiesVideos": value.proxiesVideos ? "true" : "false",
|
||||||
"invidiousCompanion": value.invidiousCompanion ? "true" : "false"
|
"invidiousCompanion": value.invidiousCompanion ? "true" : "false",
|
||||||
|
"hideVideosWithoutDuration": value.hideVideosWithoutDuration ? "true" : "false"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +36,8 @@ struct InstancesBridge: Defaults.Bridge {
|
|||||||
let frontendURL: String? = object["frontendURL"]!.isEmpty ? nil : object["frontendURL"]
|
let frontendURL: String? = object["frontendURL"]!.isEmpty ? nil : object["frontendURL"]
|
||||||
let proxiesVideos = object["proxiesVideos"] == "true"
|
let proxiesVideos = object["proxiesVideos"] == "true"
|
||||||
let invidiousCompanion = object["invidiousCompanion"] == "true"
|
let invidiousCompanion = object["invidiousCompanion"] == "true"
|
||||||
|
let hideVideosWithoutDuration = object["hideVideosWithoutDuration"] == "true"
|
||||||
|
|
||||||
return Instance(app: app, id: id, name: name, apiURLString: apiURL, frontendURL: frontendURL, proxiesVideos: proxiesVideos, invidiousCompanion: invidiousCompanion)
|
return Instance(app: app, id: id, name: name, apiURLString: apiURL, frontendURL: frontendURL, proxiesVideos: proxiesVideos, invidiousCompanion: invidiousCompanion, hideVideosWithoutDuration: hideVideosWithoutDuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,17 @@ final class InstancesModel: ObservableObject {
|
|||||||
Defaults[.instances][index] = instance
|
Defaults[.instances][index] = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setHideVideosWithoutDuration(_ instance: Instance, _ hideVideosWithoutDuration: Bool) {
|
||||||
|
guard let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = Defaults[.instances][index]
|
||||||
|
instance.hideVideosWithoutDuration = hideVideosWithoutDuration
|
||||||
|
|
||||||
|
Defaults[.instances][index] = instance
|
||||||
|
}
|
||||||
|
|
||||||
func remove(_ instance: Instance) {
|
func remove(_ instance: Instance) {
|
||||||
let accounts = accounts(instance.id)
|
let accounts = accounts(instance.id)
|
||||||
if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
|
if let index = Defaults[.instances].firstIndex(where: { $0.id == instance.id }) {
|
||||||
|
|||||||
@@ -52,11 +52,13 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configureTransformer(pathPattern("popular"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
configureTransformer(pathPattern("popular"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||||
content.json.arrayValue.map(self.extractVideo)
|
let videos = content.json.arrayValue.map(self.extractVideo)
|
||||||
|
return self.account.instance.hideVideosWithoutDuration ? videos.filter { $0.length > 0 } : videos
|
||||||
}
|
}
|
||||||
|
|
||||||
configureTransformer(pathPattern("trending"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
configureTransformer(pathPattern("trending"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||||
content.json.arrayValue.map(self.extractVideo)
|
let videos = content.json.arrayValue.map(self.extractVideo)
|
||||||
|
return self.account.instance.hideVideosWithoutDuration ? videos.filter { $0.length > 0 } : videos
|
||||||
}
|
}
|
||||||
|
|
||||||
configureTransformer(pathPattern("search"), requestMethods: [.get]) { (content: Entity<JSON>) -> SearchPage in
|
configureTransformer(pathPattern("search"), requestMethods: [.get]) { (content: Entity<JSON>) -> SearchPage in
|
||||||
@@ -70,7 +72,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
return ContentItem(playlist: self.extractChannelPlaylist(from: json))
|
return ContentItem(playlist: self.extractChannelPlaylist(from: json))
|
||||||
}
|
}
|
||||||
if type == "video" {
|
if type == "video" {
|
||||||
return ContentItem(video: self.extractVideo(from: json))
|
let video = self.extractVideo(from: json)
|
||||||
|
if self.account.instance.hideVideosWithoutDuration, video.length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ContentItem(video: video)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -101,7 +107,8 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
|
|
||||||
configureTransformer(pathPattern("auth/feed"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
configureTransformer(pathPattern("auth/feed"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||||
if let feedVideos = content.json.dictionaryValue["videos"] {
|
if let feedVideos = content.json.dictionaryValue["videos"] {
|
||||||
return feedVideos.arrayValue.map(self.extractVideo)
|
let videos = feedVideos.arrayValue.map(self.extractVideo)
|
||||||
|
return self.account.instance.hideVideosWithoutDuration ? videos.filter { $0.length > 0 } : videos
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
@@ -875,7 +882,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
return ContentItem(playlist: extractChannelPlaylist(from: json))
|
return ContentItem(playlist: extractChannelPlaylist(from: json))
|
||||||
}
|
}
|
||||||
if type == "video" {
|
if type == "video" {
|
||||||
return ContentItem(video: extractVideo(from: json))
|
let video = extractVideo(from: json)
|
||||||
|
if account.instance.hideVideosWithoutDuration, video.length == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ContentItem(video: video)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -25,11 +25,9 @@ final class SearchModel: ObservableObject {
|
|||||||
var accounts: AccountsModel { .shared }
|
var accounts: AccountsModel { .shared }
|
||||||
private var resource: Resource!
|
private var resource: Resource!
|
||||||
|
|
||||||
init() {
|
init() {}
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
deinit {}
|
||||||
}
|
|
||||||
|
|
||||||
var isLoading: Bool {
|
var isLoading: Bool {
|
||||||
resource?.isLoading ?? false
|
resource?.isLoading ?? false
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ struct InstanceSettings: View {
|
|||||||
@State private var frontendURL = ""
|
@State private var frontendURL = ""
|
||||||
@State private var proxiesVideos = false
|
@State private var proxiesVideos = false
|
||||||
@State private var invidiousCompanion = false
|
@State private var invidiousCompanion = false
|
||||||
|
@State private var hideVideosWithoutDuration = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@@ -97,6 +98,16 @@ struct InstanceSettings: View {
|
|||||||
.onChange(of: invidiousCompanion) { newValue in
|
.onChange(of: invidiousCompanion) { newValue in
|
||||||
InstancesModel.shared.setInvidiousCompanion(instance, newValue)
|
InstancesModel.shared.setInvidiousCompanion(instance, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(footer: Text("This can be used to hide shorts".localized())) {
|
||||||
|
hideVideosWithoutDurationToggle
|
||||||
|
.onAppear {
|
||||||
|
hideVideosWithoutDuration = instance.hideVideosWithoutDuration
|
||||||
|
}
|
||||||
|
.onChange(of: hideVideosWithoutDuration) { newValue in
|
||||||
|
InstancesModel.shared.setHideVideosWithoutDuration(instance, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@@ -116,6 +127,10 @@ struct InstanceSettings: View {
|
|||||||
Toggle("Invidious companion", isOn: $invidiousCompanion)
|
Toggle("Invidious companion", isOn: $invidiousCompanion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var hideVideosWithoutDurationToggle: some View {
|
||||||
|
Toggle("Experimental: Hide videos without duration", isOn: $hideVideosWithoutDuration)
|
||||||
|
}
|
||||||
|
|
||||||
private func removeAccount(_ account: Account) {
|
private func removeAccount(_ account: Account) {
|
||||||
AccountsModel.remove(account)
|
AccountsModel.remove(account)
|
||||||
accountsChanged.toggle()
|
accountsChanged.toggle()
|
||||||
|
|||||||
Reference in New Issue
Block a user