Files

209 lines
8.1 KiB
Python

import logging
import re
import scrapy
from utils.config import config_manager
from db.redis_database import REDIS_SYNC_CLIENT
class CommonTamilSpider(scrapy.Spider):
name = None
source = None
custom_settings = {
"ITEM_PIPELINES": {
"mediafusion_scrapy.pipelines.TorrentDownloadAndParsePipeline": 100,
"mediafusion_scrapy.pipelines.CatalogParsePipeline": 150,
"mediafusion_scrapy.pipelines.MovieStorePipeline": 200,
"mediafusion_scrapy.pipelines.SeriesStorePipeline": 300,
"mediafusion_scrapy.pipelines.RedisCacheURLPipeline": 400,
},
}
def __init__(
self,
pages: int = 1,
start_page: int = 1,
search_keyword: str = None,
scrap_catalog_id: str = "all",
*args,
**kwargs,
):
super(CommonTamilSpider, self).__init__(*args, **kwargs)
self.pages = pages
self.start_page = start_page
self.search_keyword = search_keyword
if scrap_catalog_id != "all" and "_" not in scrap_catalog_id:
self.logger.error(
f"Invalid catalog ID: {scrap_catalog_id}. Expected format: <language>_<video_type>"
)
return
self.scrap_catalog_id = scrap_catalog_id
logging.info(f"Scraping catalog ID: {self.scrap_catalog_id}")
self.redis = REDIS_SYNC_CLIENT
self.scraped_urls_key = f"{self.name}_scraped_urls"
self.catalogs = config_manager.get_scraper_config(self.name, "catalogs")
self.homepage = config_manager.get_scraper_config(self.name, "homepage")
self.supported_search_forums = config_manager.get_scraper_config(
self.name, "supported_search_forums"
)
def __del__(self):
self.redis.close()
def generate_forum_data(self):
data = []
link_prefix = f"{self.homepage}/index.php?/forums/forum/"
for language, catalog in self.catalogs.items():
for video_type, forum_ids in catalog.items():
scrap_links = (
[link_prefix + str(forum_id) for forum_id in forum_ids]
if isinstance(forum_ids, list)
else [link_prefix + str(forum_ids)]
)
for link in scrap_links:
for page in range(self.start_page, self.start_page + self.pages):
paginated_link = f"{link}/page/{page}/"
data.append((paginated_link, language, video_type))
return data
def start_requests(self):
if self.search_keyword:
# Construct the search URL and initiate search
search_url = f"{self.homepage}/index.php?/search/&q={self.search_keyword}&type=forums_topic&search_and_or=or&search_in=titles&sortby=relevancy"
yield scrapy.Request(search_url, self.parse_search_results)
else:
if self.scrap_catalog_id == "all":
for url, language, video_type in self.generate_forum_data():
yield scrapy.Request(
url,
self.parse_page_results,
meta={
"item": {
"language": language,
"video_type": video_type,
"source": self.source,
"expected_sources": [
self.source,
"Contribution Stream",
],
}
},
)
else:
language, video_type = self.scrap_catalog_id.split("_")
forum_ids = self.catalogs.get(language, {}).get(video_type)
if not forum_ids:
self.logger.error(f"Invalid catalog ID: {self.scrap_catalog_id}")
return
forum_links = (
[
f"{self.homepage}/index.php?/forums/forum/{forum_id}"
for forum_id in forum_ids
]
if isinstance(forum_ids, list)
else [f"{self.homepage}/index.php?/forums/forum/{forum_ids}"]
)
for forum_link in forum_links:
yield scrapy.Request(
forum_link,
self.parse_page_results,
meta={
"item": {
"language": language,
"video_type": video_type,
"source": self.source,
}
},
)
def parse_search_results(self, response):
movies = response.css("li[data-role='activityItem']")
for movie in movies:
movie_page_link = movie.css("a[data-linktype='link']::attr(href)").get()
if not movie_page_link:
continue
if not self.check_scraped_urls(movie_page_link):
yield response.follow(
movie_page_link,
self.parse_movie_page,
meta={
"item": {
"source": self.source,
"webpage_url": movie_page_link,
}
},
)
# Handling pagination
next_page_link = response.css("a[rel='next']::attr(href)").get()
if next_page_link:
yield response.follow(next_page_link, self.parse_search_results)
def parse_page_results(self, response):
movies = response.css("li[data-rowid]")
for movie in movies:
movie_page_link = movie.css("a[data-ipshover-target]::attr(href)").get()
if not movie_page_link:
continue
if not self.check_scraped_urls(movie_page_link):
item = response.meta["item"].copy()
item["webpage_url"] = movie_page_link
yield response.follow(
movie_page_link, self.parse_movie_page, meta={"item": item}
)
def check_scraped_urls(self, page_link):
if self.redis.sismember(self.scraped_urls_key, page_link):
self.logger.info(f"Skipping already scraped URL: {page_link}")
return True
return False
def parse_movie_page(self, response):
item = response.meta["item"].copy()
poster = response.css(
"div[data-commenttype='forums'] img::attr(data-src), div[data-commenttype='forums'] img::attr(src)"
).get()
created_at = response.css("time::attr(datetime)").get()
torrent_links = response.css("a[data-fileext='torrent']::attr(href)").getall()
if self.search_keyword:
forum_link = response.css("a[href*='forums/forum/']::attr(href)").get()
if not forum_link:
return
forum_id = re.search(r"forums/forum/([^/]+)/", forum_link).group(1)
language, video_type = self.supported_search_forums.get(
forum_id, {"language": None, "video_type": None}
).values()
if not language:
self.logger.error(f"Unsupported forum {forum_id}")
return
item.update({"language": language, "video_type": video_type})
if not torrent_links:
self.logger.warning(f"No torrents found for {response.url}")
self.redis.sadd(self.scraped_urls_key, response.url)
return
for torrent_link in torrent_links:
torrent_item = item.copy()
torrent_item.update(
{
"type": (
"series" if torrent_item["video_type"] == "series" else "movie"
),
"poster": poster,
"created_at": created_at,
"language": torrent_item["language"].title(),
"torrent_link": torrent_link,
"scraped_url_key": self.scraped_urls_key,
}
)
yield torrent_item