mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-29 05:48:43 +00:00
MULTI SERVER SUPPORT!!!!!!!!!!!!!!!!!!!!!
This commit is contained in:
parent
d878b13528
commit
71acdefd0b
2 changed files with 165 additions and 157 deletions
|
|
@ -33,56 +33,45 @@ extension JSController {
|
|||
Logger.shared.log(html, type: "HTMLStrings")
|
||||
if let parseFunction = self.context.objectForKeyedSubscript("extractStreamUrl"),
|
||||
let resultString = parseFunction.call(withArguments: [html]).toString() {
|
||||
if softsub {
|
||||
if let data = resultString.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
let isMultiStream = module.metadata.multiStream ?? false
|
||||
let isMultiSubs = module.metadata.multiSubs ?? false
|
||||
|
||||
var streamUrls: [String]?
|
||||
if isMultiStream, let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
if let data = resultString.data(using: .utf8) {
|
||||
do {
|
||||
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
var streamUrls: [String]? = nil
|
||||
var subtitleUrls: [String]? = nil
|
||||
|
||||
if let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
}
|
||||
|
||||
if let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var subtitleUrls: [String]?
|
||||
if isMultiSubs, let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse softsub JSON", type: "Error")
|
||||
DispatchQueue.main.async { completion((nil, nil)) }
|
||||
}
|
||||
} else {
|
||||
let moduleMetadata = self.context.objectForKeyedSubscript("module")?.objectForKeyedSubscript("metadata")
|
||||
let isMultiStream = moduleMetadata?.objectForKeyedSubscript("multiStream")?.toBool() == true
|
||||
|
||||
if isMultiStream {
|
||||
if let data = resultString.data(using: .utf8),
|
||||
let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream")
|
||||
DispatchQueue.main.async { completion((streamsArray, nil)) }
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse multi-stream JSON array", type: "Error")
|
||||
DispatchQueue.main.async { completion((nil, nil)) }
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Starting stream from: \(resultString)", type: "Stream")
|
||||
DispatchQueue.main.async { completion(([resultString], nil)) }
|
||||
}
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream from: \(resultString)", type: "Stream")
|
||||
DispatchQueue.main.async { completion(([resultString], nil)) }
|
||||
} else {
|
||||
Logger.shared.log("Failed to extract stream URL", type: "Error")
|
||||
DispatchQueue.main.async { completion((nil, nil)) }
|
||||
|
|
@ -111,65 +100,51 @@ extension JSController {
|
|||
}
|
||||
|
||||
let thenBlock: @convention(block) (JSValue) -> Void = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
guard self != nil else { return }
|
||||
|
||||
if softsub {
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
let isMultiStream = module.metadata.multiStream ?? false
|
||||
let isMultiSubs = module.metadata.multiSubs ?? false
|
||||
|
||||
var streamUrls: [String]?
|
||||
if isMultiStream, let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8) {
|
||||
do {
|
||||
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
var streamUrls: [String]? = nil
|
||||
var subtitleUrls: [String]? = nil
|
||||
|
||||
if let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
}
|
||||
|
||||
if let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var subtitleUrls: [String]?
|
||||
if isMultiSubs, let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse softsub JSON in JS", type: "Error")
|
||||
DispatchQueue.main.async {
|
||||
completion((nil, nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let moduleMetadata = self.context.objectForKeyedSubscript("module")?.objectForKeyedSubscript("metadata")
|
||||
let isMultiStream = moduleMetadata?.objectForKeyedSubscript("multiStream")?.toBool() == true
|
||||
|
||||
if isMultiStream {
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8),
|
||||
let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream")
|
||||
DispatchQueue.main.async { completion((streamsArray, nil)) }
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse multi-stream JSON array", type: "Error")
|
||||
DispatchQueue.main.async { completion((nil, nil)) }
|
||||
}
|
||||
} else {
|
||||
let streamUrl = result.toString()
|
||||
Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrl != nil ? [streamUrl!] : nil, nil))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let streamUrl = result.toString()
|
||||
Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrl != nil ? [streamUrl!] : nil, nil))
|
||||
}
|
||||
}
|
||||
|
||||
let catchBlock: @convention(block) (JSValue) -> Void = { error in
|
||||
|
|
@ -224,65 +199,51 @@ extension JSController {
|
|||
}
|
||||
|
||||
let thenBlock: @convention(block) (JSValue) -> Void = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
guard self != nil else { return }
|
||||
|
||||
if softsub {
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
let isMultiStream = module.metadata.multiStream ?? false
|
||||
let isMultiSubs = module.metadata.multiSubs ?? false
|
||||
|
||||
var streamUrls: [String]?
|
||||
if isMultiStream, let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8) {
|
||||
do {
|
||||
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
||||
var streamUrls: [String]? = nil
|
||||
var subtitleUrls: [String]? = nil
|
||||
|
||||
if let streamsArray = json["streams"] as? [String] {
|
||||
streamUrls = streamsArray
|
||||
Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream")
|
||||
} else if let streamUrl = json["stream"] as? String {
|
||||
streamUrls = [streamUrl]
|
||||
Logger.shared.log("Found single stream", type: "Stream")
|
||||
}
|
||||
|
||||
if let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var subtitleUrls: [String]?
|
||||
if isMultiSubs, let subsArray = json["subtitles"] as? [String] {
|
||||
subtitleUrls = subsArray
|
||||
Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream")
|
||||
} else if let subtitleUrl = json["subtitles"] as? String {
|
||||
subtitleUrls = [subtitleUrl]
|
||||
Logger.shared.log("Found single subtitle track", type: "Stream")
|
||||
}
|
||||
|
||||
Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrls, subtitleUrls))
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse softsub JSON in JSSecond", type: "Error")
|
||||
DispatchQueue.main.async {
|
||||
completion((nil, nil))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let moduleMetadata = self.context.objectForKeyedSubscript("module")?.objectForKeyedSubscript("metadata")
|
||||
let isMultiStream = moduleMetadata?.objectForKeyedSubscript("multiStream")?.toBool() == true
|
||||
|
||||
if isMultiStream {
|
||||
if let jsonString = result.toString(),
|
||||
let data = jsonString.data(using: .utf8),
|
||||
let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] {
|
||||
Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream")
|
||||
DispatchQueue.main.async { completion((streamsArray, nil)) }
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse multi-stream JSON array", type: "Error")
|
||||
DispatchQueue.main.async { completion((nil, nil)) }
|
||||
}
|
||||
} else {
|
||||
let streamUrl = result.toString()
|
||||
Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrl != nil ? [streamUrl!] : nil, nil))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let streamUrl = result.toString()
|
||||
Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream")
|
||||
DispatchQueue.main.async {
|
||||
completion((streamUrl != nil ? [streamUrl!] : nil, nil))
|
||||
}
|
||||
}
|
||||
|
||||
let catchBlock: @convention(block) (JSValue) -> Void = { error in
|
||||
|
|
|
|||
|
|
@ -259,8 +259,8 @@ struct MediaInfoView: View {
|
|||
Logger.shared.log("Marked episodes watched within season \(selectedSeason + 1) of \"\(title)\".", type: "General")
|
||||
}
|
||||
)
|
||||
.id(refreshTrigger)
|
||||
.disabled(isFetchingEpisode)
|
||||
.id(refreshTrigger)
|
||||
.disabled(isFetchingEpisode)
|
||||
}
|
||||
} else {
|
||||
Text("No episodes available")
|
||||
|
|
@ -299,8 +299,8 @@ struct MediaInfoView: View {
|
|||
Logger.shared.log("Marked \(ep.number - 1) episodes watched within series \"\(title)\".", type: "General")
|
||||
}
|
||||
)
|
||||
.id(refreshTrigger)
|
||||
.disabled(isFetchingEpisode)
|
||||
.id(refreshTrigger)
|
||||
.disabled(isFetchingEpisode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -448,7 +448,7 @@ struct MediaInfoView: View {
|
|||
groups.append(currentGroup)
|
||||
return groups
|
||||
}
|
||||
|
||||
|
||||
|
||||
func fetchDetails() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
|
|
@ -500,9 +500,12 @@ struct MediaInfoView: View {
|
|||
if module.metadata.softsub == true {
|
||||
if module.metadata.asyncJS == true {
|
||||
jsController.fetchStreamUrlJS(episodeUrl: href, softsub: true, module: module) { result in
|
||||
// Use first stream from array
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -513,7 +516,11 @@ struct MediaInfoView: View {
|
|||
} else if module.metadata.streamAsyncJS == true {
|
||||
jsController.fetchStreamUrlJSSecond(episodeUrl: href, softsub: true, module: module) { result in
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -524,7 +531,11 @@ struct MediaInfoView: View {
|
|||
} else {
|
||||
jsController.fetchStreamUrl(episodeUrl: href, softsub: true, module: module) { result in
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -537,7 +548,11 @@ struct MediaInfoView: View {
|
|||
if module.metadata.asyncJS == true {
|
||||
jsController.fetchStreamUrlJS(episodeUrl: href, module: module) { result in
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -548,7 +563,11 @@ struct MediaInfoView: View {
|
|||
} else if module.metadata.streamAsyncJS == true {
|
||||
jsController.fetchStreamUrlJSSecond(episodeUrl: href, module: module) { result in
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -559,7 +578,11 @@ struct MediaInfoView: View {
|
|||
} else {
|
||||
jsController.fetchStreamUrl(episodeUrl: href, module: module) { result in
|
||||
if let streams = result.streams, !streams.isEmpty {
|
||||
self.playStream(url: streams[0], fullURL: href)
|
||||
if streams.count > 1 {
|
||||
self.showStreamSelectionAlert(streams: streams, fullURL: href, subtitles: result.subtitles?.first)
|
||||
} else {
|
||||
self.playStream(url: streams[0], fullURL: href, subtitles: result.subtitles?.first)
|
||||
}
|
||||
} else {
|
||||
self.handleStreamFailure(error: nil)
|
||||
}
|
||||
|
|
@ -590,6 +613,30 @@ struct MediaInfoView: View {
|
|||
self.isLoading = false
|
||||
}
|
||||
|
||||
func showStreamSelectionAlert(streams: [String], fullURL: String, subtitles: String? = nil) {
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "Select Server", message: "Choose a server to play from", preferredStyle: .actionSheet)
|
||||
|
||||
for (index, stream) in streams.enumerated() {
|
||||
let quality = "Stream \(index + 1)"
|
||||
alert.addAction(UIAlertAction(title: quality, style: .default) { _ in
|
||||
self.playStream(url: stream, fullURL: fullURL, subtitles: subtitles)
|
||||
})
|
||||
}
|
||||
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootVC = windowScene.windows.first?.rootViewController {
|
||||
findTopViewController.findViewController(rootVC).present(alert, animated: true)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isFetchingEpisode = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func playStream(url: String, fullURL: String, subtitles: String? = nil) {
|
||||
DispatchQueue.main.async {
|
||||
let externalPlayer = UserDefaults.standard.string(forKey: "externalPlayer") ?? "Sora"
|
||||
|
|
|
|||
Loading…
Reference in a new issue