diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLAttributes.swift b/Modules/Parser/Sources/Parser/OPML/OPMLAttributes.swift
index 0669e0e53..508dd339a 100644
--- a/Modules/Parser/Sources/Parser/OPML/OPMLAttributes.swift
+++ b/Modules/Parser/Sources/Parser/OPML/OPMLAttributes.swift
@@ -10,13 +10,13 @@ import Foundation
 // OPML allows for arbitrary attributes.
 // These are the common attributes in OPML files used as RSS subscription lists.
 
-private static let opmlTextKey = "text"
-private static let opmlTitleKey = "title"
-private static let opmlDescriptionKey = "description"
-private static let opmlTypeKey = "type"
-private static let opmlVersionKey = "version"
-private static let opmlHMTLURLKey = "htmlUrl"
-private static let opmlXMLURLKey = "xmlUrl"
+private let opmlTextKey = "text"
+private let opmlTitleKey = "title"
+private let opmlDescriptionKey = "description"
+private let opmlTypeKey = "type"
+private let opmlVersionKey = "version"
+private let opmlHMTLURLKey = "htmlUrl"
+private let opmlXMLURLKey = "xmlUrl"
 
 // A frequent error in OPML files is to mess up the capitalization,
 // so these do a case-insensitive lookup.
diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift b/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift
index 647ff215c..55adb982c 100644
--- a/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift
+++ b/Modules/Parser/Sources/Parser/OPML/OPMLDocument.swift
@@ -9,6 +9,11 @@ import Foundation
 
 final class OPMLDocument: OPMLItem, @unchecked Sendable {
 
-	var title: String?
-	var url: String?
+	var title: String? = nil
+	var url: String? = nil
+
+	init(url: String?) {
+		self.url = url
+		super.init(attributes: nil)
+	}
 }
diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift b/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift
index fba59b526..001ac169a 100644
--- a/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift
+++ b/Modules/Parser/Sources/Parser/OPML/OPMLItem.swift
@@ -10,7 +10,7 @@ import os
 
 class OPMLItem: @unchecked Sendable {
 
-	public let feedSpecifier: OPMLFeedSpecifier
+	public let feedSpecifier: ParsedOPMLFeedSpecifier
 
 	public let attributes: [String: String]
 	public let titleFromAttributes: String?
@@ -20,23 +20,13 @@ class OPMLItem: @unchecked Sendable {
 		items.count > 0
 	}
 
-	init?(attributes: [String : String]) {
-
-		guard let feedURL = attributes.opml_xmlUrl, !feedURL.isEmpty else {
-			return nil
-		}
-
-		let titleFromAttributes = {
-			if let title = attributes.opml_title {
-				return title
-			}
-			return attributes.opml_text
-		}()
-		self.titleFromAttributes = titleFromAttributes
-
-		self.feedSpecifier = OPMLFeedSpecifier(title: titleFromAttributes, feedDescription: attributes.opml_description, homePageURL: attributes.opml_htmlUrl, feedURL: feedURL)
+	init(attributes: [String : String]?) {
 
+		self.titleFromAttributes = attributes.opml_title ?? attributes.opml_text
 		self.attributes = attributes
+
+		self.feedSpecifier = ParsedOPMLFeedSpecifier(title: self.titleFromAttributes, feedDescription: attributes.opml_description, homePageURL: attributes.opml_htmlUrl, feedURL: attributes.opml_xmlUrl)
+
 	}
 
 	func addItem(_ item: OPMLItem) {
diff --git a/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift b/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift
index ad34ff31b..28472fee7 100644
--- a/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift
+++ b/Modules/Parser/Sources/Parser/OPML/OPMLParser.swift
@@ -12,6 +12,7 @@ public final class OPMLParser {
 	let url: String
 	let data: Data
 
+	private let opmlDocument: OPMLDocument
 	private var itemStack = [OPMLItem]()
 	
 	enum OPMLParserError: Error {
@@ -22,9 +23,10 @@ public final class OPMLParser {
 
 		self.url = parserData.url
 		self.data = parserData.data
+		self.opmlDocument = OPMLDocument(url: parserData.url)
 	}
 
-	func parse() throws -> OPMLDocument? {
+	func parse() throws -> ParsedOPMLDocument {
 
 		guard canParseData() else {
 			throw OPMLParserError.notOPML