Skip to content

Commit

Permalink
OneDrive prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
BrutuZ committed May 8, 2023
1 parent d864ab8 commit ee28c8e
Show file tree
Hide file tree
Showing 5 changed files with 236 additions and 4 deletions.
4 changes: 2 additions & 2 deletions homepage/templates/homepage/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ <h2>While Mangadex is dead.</h2>
<div class="search-wrapper">
<div class="search-box">
<div class="cubari-logo"></div>
<input type="text" id="search" name="cubariInput" placeholder="Imgur, NH, MD, mangasee, git.io, raw GitHub link..."><button class="ico-btn icon-ri" id="search-button"></button>
<input type="text" id="search" name="cubariInput" placeholder="Imgur, NH, MD, mangasee, git.io, raw GitHub, OneDrive share link..."><button class="ico-btn icon-ri" id="search-button"></button>
</div>
<div id="status"><a href="https://old.reddit.com/r/manga/comments/s1x668/sl_cubarimoe_gist_links_are_now_deprecated">UPDATE: git.io deprecation</a><br><br>To unite several chapters under one gist link,<br>you can use this <a href="https://stirante.com/facaccimo/">useful tool</a> by Stirante.</div>
<div id="status"><a href="#" onclick="openModal('about')">UPDATE: OneDrive Shared Folders</a><br><br>To unite several chapters under one gist link,<br>you can use this <a href="https://stirante.com/facaccimo/">useful tool</a> by Stirante.</div>
</div>
</section>
</div>
Expand Down
4 changes: 3 additions & 1 deletion proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .sources.reddit import Reddit
from .sources.mangadventure import MangAdventure
from .sources.dynasty import Dynasty
from .sources.onedrive import OneDrive

sources = [
MangaDex(),
Expand All @@ -31,5 +32,6 @@
Imgbox(),
Reddit(),
Imgbb(),
Dynasty()
Dynasty(),
OneDrive(),
]
221 changes: 221 additions & 0 deletions proxy/sources/onedrive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
from datetime import datetime

from django.shortcuts import redirect
from django.urls import re_path
from django.conf import settings

from ..source import ProxySource
from ..source.data import ChapterAPI, ProxyException, SeriesAPI, SeriesPage
from ..source.helpers import api_cache, encode, get_wrapper
from json import JSONDecodeError
from requests import RequestException, HTTPError

import re

class OneDrive(ProxySource):
'''
Receives a OneDrive share URL
Will parse subfolders up to one level: "[Artist] Series Title/Ch. 01 - Chapter Title/images.ext" OR "Title/images.ext"
Expects chapter folders to be prefixed by the number, will abstact 'Ch.' and 'Chapter' prefixes
Series title will be the top-most folder name
If chapter number can't be guessed from folder title, assumes 1.
Meaning unidentified subfolders will result in a single chapter with all images
Chapter will be blank if it can't be parsed from sub-folder name
Cover will be the first `cover.ext` found in the tree or page 1 of chapter 1
Doesn't support volumes, always "Uncategorized"
'''

def get_reader_prefix(self):
return "onedrive"

def shortcut_instantiator(self):
def handler(request, series_id):
print(request, series_id)
return redirect(
f"reader-{self.get_reader_prefix()}-chapter-page", series_id)

return [
re_path(r"(?:1drv)/(?P<series_id>[\d\w]+)/$", handler),
]

@staticmethod
def date_parser(timestamp: float):
timestamp = int(timestamp)
try:
date = datetime.utcfromtimestamp(timestamp)
except ValueError:
date = datetime.utcfromtimestamp(timestamp // 1000)
return [
date.year,
date.month - 1,
date.day,
date.hour,
date.minute,
date.second,
]

@api_cache(prefix="od_common_dt", time=300)
def od_common(self, meta_id):

def od_api(share_id: str) -> dict:
map = {'folders': [], 'files': []}
od_series_api = f"https://api.onedrive.com/v1.0/shares/{share_id}/driveItem?$expand=children"
resp = get_wrapper(od_series_api)
print(f'Response code: {resp.status_code} {resp.url}')

if not resp.ok:
resp = get_wrapper(od_series_api, use_proxy=True)
print(f'Response code proxy: {resp.status_code} {resp.url}')

try:
resp.raise_for_status()
resp = resp.json()
except (HTTPError, JSONDecodeError, RequestException) as error:
raise ProxyException(f'Could not parse OneDrive folder `{share_id}`: {error}')

map['title'] = resp['name']
try:
map['date'] = datetime.fromisoformat(
f"{resp.get('lastModifiedDateTime', '')[:19]}+00:00"
).timestamp()
except ValueError:
map['date'] = datetime.utcnow().timestamp()

for contents in resp['children']:
if 'file' in contents and (
'image' in contents
or 'image' in contents.get('file', {}).get('mimeType', '')
):
if contents['name'].startswith('cover.'):
map['cover'] = contents['@content.downloadUrl']
continue
map['files'].append(contents['@content.downloadUrl'])
if 'folder' in contents:
map['folders'].append(contents.get('webUrl').split('/')[-1])
if not map.get('cover') and map['files']:
map['cover'] = map['files'][0]
return map

chapters_dict = {
'1': {
'title': '',
'last_updated': None,
'groups': {
'OneDrive': []
}
}
}
series_dict = {
'title': '',
'description': '',
'artist': None,
'author': None,
'cover': None,
}
series = od_api(meta_id)
series_dict['title'] = self.parse_title(series['title'])[1]
has_artist = re.search(r'^\[(.+?)\] ', series['title'], re.IGNORECASE)
series_dict['artist'] = has_artist.group(1) if has_artist else 'Unknown'
series_dict['alt_title'] = series_dict['title'].replace(has_artist.group(), '') if has_artist else ''
series_dict['cover'] = series.get('cover')

if series.get('files'):
chapters_dict['1'] = {
'title': series['title'],
'last_updated': series['date'],
'groups': {
'OneDrive': series['files']
}
}

for subfolder in series.get('folders', {}):
folder = od_api(subfolder)
if not folder['files']:
continue
if not series_dict['cover']:
series_dict['cover'] = folder['files'][0]
title = self.parse_title(folder['title'])
chapters_dict[title[0]] = {
'title': title[1],
'last_updated': folder['date'],
'groups': {'OneDrive': folder['files']}
}
series_dict['chapters'] = chapters_dict

chapter_list = [
[
ch[0], # Chapter Number
ch[0], # Chapter Number
ch[1]["title"], # Chapter Title
ch[0].replace(".", "-"), # Chapter Slug
"OneDrive", # Group
self.date_parser(ch[1]["last_updated"]), # Date
'Uncatecorized', # Volume Number
]
for ch in sorted(
chapters_dict.items(),
key=lambda m: float(m[0]),
# reverse=True,
)
]
groups_dict = {str(key): 'OneDrive' for key in chapters_dict}

return {
"slug": meta_id,
"title": series_dict['title'],
"alt_title": series_dict['alt_title'],
"description": "",
"artist": series_dict['artist'],
"cover": series_dict['cover'],
"chapters": chapters_dict,
"chapter_list": chapter_list,
'groups': groups_dict,
"timestamp": series['date'],
}

def parse_title(self, title: str) -> tuple:
search = re.search(
r'^(?:Ch\.? ?|Chapter )?0?([\d\.,]{1,5})(?: - )?', title, re.IGNORECASE
)
ch = search.group(1) if search else '1'
ch_title = title if not search else title.replace(search.group(), '')
return (ch, ch_title)

@api_cache(prefix="od_series_dt", time=300)
def series_api_handler(self, meta_id):
data = self.od_common(meta_id)
return SeriesAPI(
slug=meta_id,
title=data["title"],
description=data["description"],
author=data["artist"],
artist=data["artist"],
groups=data['groups'],
cover=data["cover"],
chapters=data["chapters"],
) if data else None

@api_cache(prefix="od_pages_dt", time=300)
def chapter_api_handler(self, meta_id):
data = self.od_common(meta_id)
return ChapterAPI(
pages=data['chapters']['1']['groups']['OneDrive'],
series=data['slug'],
chapter='1',
) if data else None

@api_cache(prefix="od_series_page_dt", time=300)
def series_page_handler(self, meta_id):
data = self.od_common(meta_id)
return SeriesPage(
series=data["title"],
alt_titles=[data["alt_title"]],
alt_titles_str=data["alt_title"],
slug=data["slug"],
cover_vol_url=data["cover"],
metadata=[["Author", data["artist"]], ["Artist", data["artist"]]],
synopsis=data['description'],
author=data["artist"],
chapter_list=data['chapter_list'],
original_url=f"https://1drv.ms/f/{meta_id}",
) if data else None
6 changes: 6 additions & 0 deletions static_global/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ let error = '';
if(!result || !result[2]) return message('Reader could not understand the given link.', 1)
result = '/read/nhentai/' + result[2];
break;
case (/1drv\.ms\/f\/s![A-Z0-9a-z]+/.test(text)):
result = /1drv\.ms\/f\/s!([A-Z0-9a-z]+)\b/.exec(text);
console.log(result)
if (!result || !result[1]) return message('Reader could not understand the given link.', 1)
result = '/read/onedrive/' + result[1];
break;
case (/mangadex\.org\/title/.test(text)):
result = /(\/?)([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/.exec(text)
if(!result || !result[2]) return message('Reader could not understand the given link.', 1)
Expand Down
5 changes: 4 additions & 1 deletion templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ <h2>What's this thing?</h2>
<p>This reader was born while developing Guya.moe, the website to host Kaguya. After a while we added a lot of useful features, and now we branched it into a separate domain. We don't host anything, this is just a web image viewer that can parse other websites.</p>

<h2>What are the sites you can proxy?</h2>
<p>Currently it supports imgur, nhentai and custom appropriately-formatted JSON hosted on GitHub Gists. This is an advanced feature and will be explained in detail below.</p>
<p>Currently it supports imgur, OneDrive, nhentai and custom appropriately-formatted JSON hosted on GitHub Gists. This is an advanced feature and will be explained in detail below.</p>

<h2>Disclaimer regarding OneDrive shares</h2>
<p>OneDrive links provide user information (look for the "More Details" pane on the "Info" tab).<br>Keep that in mind if you intend to share Cubari links from that source.<br>We can't opt-out of receiving that information, so the best we can do is pinky-swear we don't even read it.</p>

<h2>Are you the next Mangadex?</h2>
<p>No, but we might become a web version Tachiyomi. This is not a guarantee, though.</p>
Expand Down

0 comments on commit ee28c8e

Please sign in to comment.