Skip to content
This repository was archived by the owner on Nov 14, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// This file is part of the NineAnimator project.
//
// Copyright © 2018-2020 Marcus Zhou. All rights reserved.
//
// NineAnimator is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// NineAnimator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with NineAnimator. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
import NineAnimatorCommon
import SwiftSoup

extension NASourceAnimeFlv {
func anime(from link: AnimeLink) -> NineAnimatorPromise<Anime> {
self.requestManager.request(
url: link.link, handling: .browsing)
.responseString
.then {
responseContent -> Anime in
let bowl = try SwiftSoup.parse(responseContent)
let animeTitle = try bowl.select("h1.Title").text()
let animeArtworkURL = try URL(
string: bowl.select("figure.Image > img").attr("src"),
relativeTo: self.endpointURL
) ?? link.image
let reconstructedAnimeLink = AnimeLink(
title: animeTitle,
link: link.link,
image: animeArtworkURL,
source: self
)
// List of episode
let episodeList = try bowl.select("li.Episode").compactMap {
episodeElement -> (identifier: String, episodeNumber: String) in
let episodeIdentifier = "\(self.endpointURL)\(try episodeElement.select("a").attr("href"))"
let episodeNumber = try episodeElement
.select("a")
.text()
.replacingOccurrences(of: animeTitle, with: "")
return (episodeIdentifier, episodeNumber)
} .reversed()

if episodeList.isEmpty {
throw NineAnimatorError.responseError("No episodes found for this anime")
}

// Collection of episodes
var episodeCollection = Anime.EpisodesCollection()

// We incorrectly assume each server contains every episode
for (serverIdentifier, _) in NASourceAnimeFlv.knownServers {
var currentCollection = [EpisodeLink]()

for (episodeIdentifier, episodeName) in episodeList {
let currentEpisodeLink = EpisodeLink(
identifier: episodeIdentifier,
name: episodeName,
server: serverIdentifier,
parent: reconstructedAnimeLink
)
currentCollection.append(currentEpisodeLink)
}

episodeCollection[serverIdentifier] = currentCollection.reversed()
}

// Information
let animeSynopsis = try bowl.select("header > p:nth-child(3)").text().replacingOccurrences(of: "Sinopsis: ", with: "")

// Attributes
var additionalAnimeAttributes = [Anime.AttributeKey: Any]()

let rating = try bowl.select("#votes_prmd").text()
additionalAnimeAttributes[.rating] = Float(rating) ?? 0
additionalAnimeAttributes[.ratingScale] = Float(5.0)

return Anime(
reconstructedAnimeLink,
alias: "",
additionalAttributes: additionalAnimeAttributes,
description: animeSynopsis,
on: NASourceAnimeFlv.knownServers,
episodes: episodeCollection
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// This file is part of the NineAnimator project.
//
// Copyright © 2018-2020 Marcus Zhou. All rights reserved.
//
// NineAnimator is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// NineAnimator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with NineAnimator. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
import NineAnimatorCommon
import SwiftSoup

extension NASourceAnimeFlv {
static let knownServers = [
"yuserver": "YUserver"
]

static let urlRegex = try! NSRegularExpression(pattern: "https:\\\\/\\\\/www.+?(?=\")")

func episode(from link: EpisodeLink, with anime: Anime) -> NineAnimatorPromise<Episode> {
self.requestManager.request(url: link.identifier, handling: .browsing)
.responseString
.then {
responseContent in
let bowl = try SwiftSoup.parse(responseContent)
let scriptText = try bowl.select("#AnimeFlv > script:nth-child(16)").outerHtml()
let iframeURLString = try (NASourceAnimeFlv.urlRegex.firstMatch(in: scriptText))
.tryUnwrap()[0].replacingOccurrences(of: "\\", with: "")
let iframeURL = try URL(string: iframeURLString).tryUnwrap()
return Episode(
link,
target: iframeURL,
parent: anime
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//
// This file is part of the NineAnimator project.
//
// Copyright © 2018-2020 Marcus Zhou. All rights reserved.
//
// NineAnimator is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// NineAnimator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with NineAnimator. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
import NineAnimatorCommon
import SwiftSoup

extension NASourceAnimeFlv {
func featured() -> NineAnimatorPromise<FeaturedContainer> {
self.requestManager.request(
url: endpointURL,
handling: .browsing
).responseString.then {
responseContent in
let bowl = try SwiftSoup.parse(responseContent)
let featuredAnime = try bowl.select("ul.List-Animes")
.first()
.tryUnwrap(NineAnimatorError.decodeError("Cannot retrieve featured anime"))
.select("li")
.map {
animeContainer -> AnimeLink in

let animeArtworkURL = try URL(
string: animeContainer.select("a > figure.Image > img").attr("src"),
relativeTo: self.endpointURL
) ?? NineAnimator.placeholderArtworkUrl
let animeLink = try URL(
string: animeContainer.select("a").attr("href"),
relativeTo: self.endpointURL
).tryUnwrap(NineAnimatorError.urlError)

let animeTitle = try animeContainer.select("a h2.Title").text()
return AnimeLink(
title: animeTitle,
link: animeLink,
image: animeArtworkURL,
source: self
)
}
/*
let ongoingAnime = try bowl.select("ul.List-Episodes")
.first()
.tryUnwrap(NineAnimatorError.decodeError("Cannot retrieve latest anime"))
.select("li")
.map {
animeContainer -> AnimeLink in

let animeArtworkURL = try URL(
string: animeContainer.select("a figure.Image > img").attr("src"),
relativeTo: self.endpointURL
) ?? NineAnimator.placeholderArtworkUrl

let animeLink = try URL(
string: animeContainer.select("a").attr("href"),
relativeTo: self.endpointURL
).tryUnwrap(NineAnimatorError.urlError)

let animeTitle = try animeContainer.select("a h2.Title").text()

return AnimeLink(
title: animeTitle,
link: animeLink,
image: animeArtworkURL,
source: self
)
}
*/

return BasicFeaturedContainer(
featured: featuredAnime,
latest: []
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// This file is part of the NineAnimator project.
//
// Copyright © 2018-2020 Marcus Zhou. All rights reserved.
//
// NineAnimator is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// NineAnimator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with NineAnimator. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation
import NineAnimatorCommon
import SwiftSoup

extension NASourceAnimeFlv {
class SearchAgent: ContentProvider {
var title: String

var totalPages: Int? { 1 }
var availablePages: Int { _results == nil ? 0 : 1 }
var moreAvailable: Bool { _results == nil }

weak var delegate: ContentProviderDelegate?
private var parent: NASourceAnimeFlv
private var performingTask: NineAnimatorAsyncTask?
private var _results: [AnimeLink]?

func links(on page: Int) -> [AnyLink] {
page == 0 ? _results?.map { .anime($0) } ?? [] : []
}

func more() {
if performingTask == nil {
// Animehub includes the search title in the URL path, rather than as a URL query param.
let encodedTitle = self.title.replacingOccurrences(of: " ", with: "+")

performingTask = parent.requestManager.request(
"/browse?q=\(encodedTitle)",
handling: .browsing
).responseString.then {
[parent] responseContent -> [AnimeLink]? in
try SwiftSoup.parse(responseContent)
.select("ul.List-Animes > li.Anime").compactMap {
animeContainer -> AnimeLink? in
let animeArtworkURL = try URL(
string: animeContainer.select("a > figure.Image > img").attr("src"),
relativeTo: self.parent.endpointURL
) ?? NineAnimator.placeholderArtworkUrl

let animeLink = try URL(
string: animeContainer.select("a").attr("href"),
relativeTo: self.parent.endpointURL
).tryUnwrap(NineAnimatorError.urlError)

let animeTitle = try animeContainer.select("a > h2.Title").text()

return AnimeLink(
title: animeTitle,
link: animeLink,
image: animeArtworkURL,
source: parent
)
}
} .then {
results -> [AnimeLink] in
if results.isEmpty {
throw NineAnimatorError.searchError("No results found")
} else { return results }
} .error {
[weak self] in
guard let self = self else { return }

// Reset performing task if the error is not 404
if !($0 is NineAnimatorError.SearchError) {
self.performingTask = nil
}

self.delegate?.onError($0, from: self)
} .finally {
[weak self] in
guard let self = self else { return }
self._results = $0
self.delegate?.pageIncoming(0, from: self)
}
}
}
init(_ query: String, withParent parent: NASourceAnimeFlv) {
self.parent = parent
self.title = query
}
}
func search(keyword: String) -> ContentProvider {
SearchAgent(keyword, withParent: self)
}
}
Loading