Sources: Add subName and fixup

The subName parameter is for aggregate sources that pull from a
child website. Make it so it's possible to include that child
site in parsers.

Also remove the magnet link/hash requirement since it's filtered out
anyways after results are fetched.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-02-08 22:10:02 -05:00
parent 41572362c7
commit 88a2dc9742
7 changed files with 93 additions and 28 deletions

View file

@ -22,6 +22,7 @@ public extension SourceHtmlParser {
@NSManaged var seedLeech: SourceSeedLeech? @NSManaged var seedLeech: SourceSeedLeech?
@NSManaged var size: SourceSize? @NSManaged var size: SourceSize?
@NSManaged var title: SourceTitle? @NSManaged var title: SourceTitle?
@NSManaged var subName: SourceSubName?
} }
extension SourceHtmlParser: Identifiable {} extension SourceHtmlParser: Identifiable {}

View file

@ -23,6 +23,7 @@ public extension SourceJsonParser {
@NSManaged var seedLeech: SourceSeedLeech? @NSManaged var seedLeech: SourceSeedLeech?
@NSManaged var size: SourceSize? @NSManaged var size: SourceSize?
@NSManaged var title: SourceTitle? @NSManaged var title: SourceTitle?
@NSManaged var subName: SourceSubName?
} }
extension SourceJsonParser: Identifiable {} extension SourceJsonParser: Identifiable {}

View file

@ -23,6 +23,7 @@ public extension SourceRssParser {
@NSManaged var seedLeech: SourceSeedLeech? @NSManaged var seedLeech: SourceSeedLeech?
@NSManaged var size: SourceSize? @NSManaged var size: SourceSize?
@NSManaged var title: SourceTitle? @NSManaged var title: SourceTitle?
@NSManaged var subName: SourceSubName?
} }
extension SourceRssParser: Identifiable {} extension SourceRssParser: Identifiable {}

View file

@ -99,6 +99,7 @@
<relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="htmlParser" inverseEntity="Source"/> <relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="htmlParser" inverseEntity="Source"/>
<relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentHtmlParser" inverseEntity="SourceSeedLeech"/> <relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentHtmlParser" inverseEntity="SourceSeedLeech"/>
<relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentHtmlParser" inverseEntity="SourceSize"/> <relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentHtmlParser" inverseEntity="SourceSize"/>
<relationship name="subName" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceSubName" inverseName="parentHtmlParser" inverseEntity="SourceSubName"/>
<relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentHtmlParser" inverseEntity="SourceTitle"/> <relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentHtmlParser" inverseEntity="SourceTitle"/>
</entity> </entity>
<entity name="SourceJsonParser" representedClassName="SourceJsonParser" syncable="YES"> <entity name="SourceJsonParser" representedClassName="SourceJsonParser" syncable="YES">
@ -110,6 +111,7 @@
<relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="jsonParser" inverseEntity="Source"/> <relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="jsonParser" inverseEntity="Source"/>
<relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentJsonParser" inverseEntity="SourceSeedLeech"/> <relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentJsonParser" inverseEntity="SourceSeedLeech"/>
<relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentJsonParser" inverseEntity="SourceSize"/> <relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentJsonParser" inverseEntity="SourceSize"/>
<relationship name="subName" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceSubName" inverseName="parentJsonParser" inverseEntity="SourceSubName"/>
<relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentJsonParser" inverseEntity="SourceTitle"/> <relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentJsonParser" inverseEntity="SourceTitle"/>
</entity> </entity>
<entity name="SourceMagnetHash" representedClassName="SourceMagnetHash" parentEntity="SourceComplexQuery" syncable="YES" codeGenerationType="class"> <entity name="SourceMagnetHash" representedClassName="SourceMagnetHash" parentEntity="SourceComplexQuery" syncable="YES" codeGenerationType="class">
@ -132,6 +134,7 @@
<relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="rssParser" inverseEntity="Source"/> <relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="rssParser" inverseEntity="Source"/>
<relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentRssParser" inverseEntity="SourceSeedLeech"/> <relationship name="seedLeech" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSeedLeech" inverseName="parentRssParser" inverseEntity="SourceSeedLeech"/>
<relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentRssParser" inverseEntity="SourceSize"/> <relationship name="size" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceSize" inverseName="parentRssParser" inverseEntity="SourceSize"/>
<relationship name="subName" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceSubName" inverseName="parentRssParser" inverseEntity="SourceSubName"/>
<relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentRssParser" inverseEntity="SourceTitle"/> <relationship name="title" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceTitle" inverseName="parentRssParser" inverseEntity="SourceTitle"/>
</entity> </entity>
<entity name="SourceSeedLeech" representedClassName="SourceSeedLeech" syncable="YES"> <entity name="SourceSeedLeech" representedClassName="SourceSeedLeech" syncable="YES">
@ -151,6 +154,11 @@
<relationship name="parentJsonParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceJsonParser" inverseName="size" inverseEntity="SourceJsonParser"/> <relationship name="parentJsonParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceJsonParser" inverseName="size" inverseEntity="SourceJsonParser"/>
<relationship name="parentRssParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceRssParser" inverseName="size" inverseEntity="SourceRssParser"/> <relationship name="parentRssParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceRssParser" inverseName="size" inverseEntity="SourceRssParser"/>
</entity> </entity>
<entity name="SourceSubName" representedClassName="SourceSubName" parentEntity="SourceComplexQuery" syncable="YES" codeGenerationType="class">
<relationship name="parentHtmlParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceHtmlParser" inverseName="subName" inverseEntity="SourceHtmlParser"/>
<relationship name="parentJsonParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceJsonParser" inverseName="subName" inverseEntity="SourceJsonParser"/>
<relationship name="parentRssParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceRssParser" inverseName="subName" inverseEntity="SourceRssParser"/>
</entity>
<entity name="SourceTitle" representedClassName="SourceTitle" parentEntity="SourceComplexQuery" syncable="YES" codeGenerationType="class"> <entity name="SourceTitle" representedClassName="SourceTitle" parentEntity="SourceComplexQuery" syncable="YES" codeGenerationType="class">
<relationship name="parentHtmlParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceHtmlParser" inverseName="title" inverseEntity="SourceHtmlParser"/> <relationship name="parentHtmlParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceHtmlParser" inverseName="title" inverseEntity="SourceHtmlParser"/>
<relationship name="parentJsonParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceJsonParser" inverseName="title" inverseEntity="SourceJsonParser"/> <relationship name="parentJsonParser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SourceJsonParser" inverseName="title" inverseEntity="SourceJsonParser"/>

View file

@ -18,7 +18,7 @@ public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
let minVersion: String? let minVersion: String?
let baseUrl: String? let baseUrl: String?
let fallbackUrls: [String]? let fallbackUrls: [String]?
var dynamicBaseUrl: Bool? let dynamicBaseUrl: Bool?
let trackers: [String]? let trackers: [String]?
let api: SourceApiJson? let api: SourceApiJson?
let jsonParser: SourceJsonParserJson? let jsonParser: SourceJsonParserJson?
@ -62,10 +62,11 @@ public struct SourceJsonParserJson: Codable, Hashable, Sendable {
let searchUrl: String let searchUrl: String
let results: String? let results: String?
let subResults: String? let subResults: String?
let magnetHash: SouceComplexQueryJson? let magnetHash: SourceComplexQueryJson?
let magnetLink: SouceComplexQueryJson? let magnetLink: SourceComplexQueryJson?
let title: SouceComplexQueryJson? let subName: SourceComplexQueryJson?
let size: SouceComplexQueryJson? let title: SourceComplexQueryJson?
let size: SourceComplexQueryJson?
let sl: SourceSLJson? let sl: SourceSLJson?
} }
@ -73,10 +74,11 @@ public struct SourceRssParserJson: Codable, Hashable, Sendable {
let rssUrl: String? let rssUrl: String?
let searchUrl: String let searchUrl: String
let items: String let items: String
let magnetHash: SouceComplexQueryJson? let magnetHash: SourceComplexQueryJson?
let magnetLink: SouceComplexQueryJson? let magnetLink: SourceComplexQueryJson?
let title: SouceComplexQueryJson? let subName: SourceComplexQueryJson?
let size: SouceComplexQueryJson? let title: SourceComplexQueryJson?
let size: SourceComplexQueryJson?
let sl: SourceSLJson? let sl: SourceSLJson?
} }
@ -84,12 +86,13 @@ public struct SourceHtmlParserJson: Codable, Hashable, Sendable {
let searchUrl: String let searchUrl: String
let rows: String let rows: String
let magnet: SourceMagnetJson let magnet: SourceMagnetJson
let title: SouceComplexQueryJson? let subName: SourceComplexQueryJson?
let size: SouceComplexQueryJson? let title: SourceComplexQueryJson?
let size: SourceComplexQueryJson?
let sl: SourceSLJson? let sl: SourceSLJson?
} }
public struct SouceComplexQueryJson: Codable, Hashable, Sendable { public struct SourceComplexQueryJson: Codable, Hashable, Sendable {
let query: String let query: String
let discriminator: String? let discriminator: String?
let attribute: String? let attribute: String?

View file

@ -45,6 +45,7 @@ public class PluginManager: ObservableObject {
minVersion: inputJson.minVersion, minVersion: inputJson.minVersion,
baseUrl: inputJson.baseUrl, baseUrl: inputJson.baseUrl,
fallbackUrls: inputJson.fallbackUrls, fallbackUrls: inputJson.fallbackUrls,
dynamicBaseUrl: inputJson.dynamicBaseUrl,
trackers: inputJson.trackers, trackers: inputJson.trackers,
api: inputJson.api, api: inputJson.api,
jsonParser: inputJson.jsonParser, jsonParser: inputJson.jsonParser,
@ -257,7 +258,6 @@ public class PluginManager: ObservableObject {
// If there's no base URL and it isn't dynamic, return before any transactions occur // If there's no base URL and it isn't dynamic, return before any transactions occur
let dynamicBaseUrl = sourceJson.dynamicBaseUrl ?? false let dynamicBaseUrl = sourceJson.dynamicBaseUrl ?? false
if !dynamicBaseUrl, sourceJson.baseUrl == nil { if !dynamicBaseUrl, sourceJson.baseUrl == nil {
await toastModel?.updateToastDescription("Not adding this source because base URL parameters are malformed. Please contact the source dev.") await toastModel?.updateToastDescription("Not adding this source because base URL parameters are malformed. Please contact the source dev.")
print("Not adding source \(sourceJson.name) because base URL parameters are malformed") print("Not adding source \(sourceJson.name) because base URL parameters are malformed")
@ -401,6 +401,15 @@ public class PluginManager: ObservableObject {
newSourceJsonParser.magnetHash = newSourceMagnetHash newSourceJsonParser.magnetHash = newSourceMagnetHash
} }
if let subNameJson = jsonParserJson.subName {
let newSourceSubName = SourceSubName(context: backgroundContext)
newSourceSubName.query = subNameJson.query
newSourceSubName.attribute = subNameJson.query
newSourceSubName.discriminator = subNameJson.discriminator
newSourceJsonParser.subName = newSourceSubName
}
if let titleJson = jsonParserJson.title { if let titleJson = jsonParserJson.title {
let newSourceTitle = SourceTitle(context: backgroundContext) let newSourceTitle = SourceTitle(context: backgroundContext)
newSourceTitle.query = titleJson.query newSourceTitle.query = titleJson.query
@ -448,6 +457,7 @@ public class PluginManager: ObservableObject {
newSourceMagnetLink.query = magnetLinkJson.query newSourceMagnetLink.query = magnetLinkJson.query
newSourceMagnetLink.attribute = magnetLinkJson.attribute ?? "text" newSourceMagnetLink.attribute = magnetLinkJson.attribute ?? "text"
newSourceMagnetLink.discriminator = magnetLinkJson.discriminator newSourceMagnetLink.discriminator = magnetLinkJson.discriminator
newSourceMagnetLink.regex = magnetLinkJson.regex
newSourceRssParser.magnetLink = newSourceMagnetLink newSourceRssParser.magnetLink = newSourceMagnetLink
} }
@ -457,15 +467,27 @@ public class PluginManager: ObservableObject {
newSourceMagnetHash.query = magnetHashJson.query newSourceMagnetHash.query = magnetHashJson.query
newSourceMagnetHash.attribute = magnetHashJson.attribute ?? "text" newSourceMagnetHash.attribute = magnetHashJson.attribute ?? "text"
newSourceMagnetHash.discriminator = magnetHashJson.discriminator newSourceMagnetHash.discriminator = magnetHashJson.discriminator
newSourceMagnetHash.regex = magnetHashJson.regex
newSourceRssParser.magnetHash = newSourceMagnetHash newSourceRssParser.magnetHash = newSourceMagnetHash
} }
if let subNameJson = rssParserJson.subName {
let newSourceSubName = SourceSubName(context: backgroundContext)
newSourceSubName.query = subNameJson.query
newSourceSubName.attribute = subNameJson.attribute ?? "text"
newSourceSubName.discriminator = subNameJson.discriminator
newSourceSubName.regex = subNameJson.regex
newSourceRssParser.subName = newSourceSubName
}
if let titleJson = rssParserJson.title { if let titleJson = rssParserJson.title {
let newSourceTitle = SourceTitle(context: backgroundContext) let newSourceTitle = SourceTitle(context: backgroundContext)
newSourceTitle.query = titleJson.query newSourceTitle.query = titleJson.query
newSourceTitle.attribute = titleJson.attribute ?? "text" newSourceTitle.attribute = titleJson.attribute ?? "text"
newSourceTitle.discriminator = titleJson.discriminator newSourceTitle.discriminator = titleJson.discriminator
newSourceTitle.regex = titleJson.regex
newSourceRssParser.title = newSourceTitle newSourceRssParser.title = newSourceTitle
} }
@ -475,6 +497,7 @@ public class PluginManager: ObservableObject {
newSourceSize.query = sizeJson.query newSourceSize.query = sizeJson.query
newSourceSize.attribute = sizeJson.attribute ?? "text" newSourceSize.attribute = sizeJson.attribute ?? "text"
newSourceSize.discriminator = sizeJson.discriminator newSourceSize.discriminator = sizeJson.discriminator
newSourceSize.regex = sizeJson.regex
newSourceRssParser.size = newSourceSize newSourceRssParser.size = newSourceSize
} }
@ -502,6 +525,15 @@ public class PluginManager: ObservableObject {
newSourceHtmlParser.searchUrl = htmlParserJson.searchUrl newSourceHtmlParser.searchUrl = htmlParserJson.searchUrl
newSourceHtmlParser.rows = htmlParserJson.rows newSourceHtmlParser.rows = htmlParserJson.rows
if let subNameJson = htmlParserJson.subName {
let newSourceSubName = SourceSubName(context: backgroundContext)
newSourceSubName.query = subNameJson.query
newSourceSubName.attribute = subNameJson.attribute ?? "text"
newSourceSubName.regex = subNameJson.regex
newSourceHtmlParser.subName = newSourceSubName
}
// Adds a title complex query if present // Adds a title complex query if present
if let titleJson = htmlParserJson.title { if let titleJson = htmlParserJson.title {
let newSourceTitle = SourceTitle(context: backgroundContext) let newSourceTitle = SourceTitle(context: backgroundContext)

View file

@ -399,6 +399,12 @@ class ScrapingViewModel: ObservableObject {
} }
} }
var subName: String?
if let subNameParser = jsonParser.subName {
let rawSubName = result[subNameParser.query.components(separatedBy: ".")].rawValue
subName = rawSubName is NSNull ? nil : String(describing: rawSubName)
}
var link: String? = existingSearchResult?.magnet.link var link: String? = existingSearchResult?.magnet.link
if let magnetLinkParser = jsonParser.magnetLink, link == nil { if let magnetLinkParser = jsonParser.magnetLink, link == nil {
let rawLink = result[magnetLinkParser.query.components(separatedBy: ".")].rawValue let rawLink = result[magnetLinkParser.query.components(separatedBy: ".")].rawValue
@ -432,7 +438,7 @@ class ScrapingViewModel: ObservableObject {
let result = SearchResult( let result = SearchResult(
title: title, title: title,
source: source.name, source: subName.map { "\(source.name) - \($0)" } ?? source.name,
size: size, size: size,
magnet: Magnet(hash: magnetHash, link: link, title: title, trackers: source.trackers), magnet: Magnet(hash: magnetHash, link: link, title: title, trackers: source.trackers),
seeders: seeders, seeders: seeders,
@ -474,6 +480,18 @@ class ScrapingViewModel: ObservableObject {
) )
} }
// Fetches the subName for the source if there is one
var subName: String?
if let subNameParser = rssParser.subName {
subName = try? runRssComplexQuery(
item: item,
query: subNameParser.query,
attribute: subNameParser.attribute,
discriminator: subNameParser.discriminator,
regexString: subNameParser.regex
)
}
var title: String? var title: String?
if let titleParser = rssParser.title { if let titleParser = rssParser.title {
title = try? runRssComplexQuery( title = try? runRssComplexQuery(
@ -485,9 +503,9 @@ class ScrapingViewModel: ObservableObject {
) )
} }
var link: String? var href: String?
if let magnetLinkParser = rssParser.magnetLink { if let magnetLinkParser = rssParser.magnetLink {
link = try? runRssComplexQuery( href = try? runRssComplexQuery(
item: item, item: item,
query: magnetLinkParser.query, query: magnetLinkParser.query,
attribute: magnetLinkParser.attribute, attribute: magnetLinkParser.attribute,
@ -498,10 +516,6 @@ class ScrapingViewModel: ObservableObject {
continue continue
} }
guard let href = link, href.starts(with: "magnet:") else {
continue
}
var size: String? var size: String?
if let sizeParser = rssParser.size { if let sizeParser = rssParser.size {
size = try? runRssComplexQuery( size = try? runRssComplexQuery(
@ -543,7 +557,7 @@ class ScrapingViewModel: ObservableObject {
let result = SearchResult( let result = SearchResult(
title: title ?? "No title", title: title ?? "No title",
source: source.name, source: subName.map { "\(source.name) - \($0)" } ?? source.name,
size: size ?? "", size: size ?? "",
magnet: Magnet(hash: magnetHash, link: href, title: title, trackers: source.trackers), magnet: Magnet(hash: magnetHash, link: href, title: title, trackers: source.trackers),
seeders: seeders, seeders: seeders,
@ -649,10 +663,6 @@ class ScrapingViewModel: ObservableObject {
href = link href = link
} }
if !href.starts(with: "magnet:") {
continue
}
// Fetches the episode/movie title // Fetches the episode/movie title
var title: String? var title: String?
if let titleParser = htmlParser.title { if let titleParser = htmlParser.title {
@ -664,8 +674,17 @@ class ScrapingViewModel: ObservableObject {
) )
} }
// Fetches the torrent's size var subName: String?
// TODO: Add int translation if let subNameParser = htmlParser.subName {
subName = try? runHtmlComplexQuery(
row: row,
query: subNameParser.query,
attribute: subNameParser.attribute,
regexString: subNameParser.regex
)
}
// Fetches the size
var size: String? var size: String?
if let sizeParser = htmlParser.size { if let sizeParser = htmlParser.size {
size = try? runHtmlComplexQuery( size = try? runHtmlComplexQuery(
@ -718,7 +737,7 @@ class ScrapingViewModel: ObservableObject {
let result = SearchResult( let result = SearchResult(
title: title ?? "No title", title: title ?? "No title",
source: source.name, source: subName.map { "\(source.name) - \($0)" } ?? source.name,
size: size ?? "", size: size ?? "",
magnet: Magnet(hash: nil, link: href), magnet: Magnet(hash: nil, link: href),
seeders: seeders, seeders: seeders,