torrent work

This commit is contained in:
ThaUnknown 2022-03-11 22:20:45 +01:00
parent 3f5b0f5f2e
commit 12fd3cdcfc
14 changed files with 229 additions and 205 deletions

View file

@ -18,7 +18,7 @@
"svelte": "^3.46.4",
"vite": "^2.8.6",
"vite-plugin-commonjs-externals": "^0.1.1",
"webtorrent": "^1.8.5"
"webtorrent": "^1.7.2"
},
"standard": {
"ignore": [

View file

@ -14,7 +14,7 @@
<script defer type="module" src="/src/main.js"></script>
</head>
<body class="dark-mode with-custom-webkit-scrollbars with-custom-css-scrollbars" data-sidebar-shortcut-enabled="true">
<body class="dark-mode with-custom-webkit-scrollbars with-custom-css-scrollbars">
</body>
</html>

View file

@ -44,10 +44,20 @@
overscroll-behavior: none;
user-select: none;
}
:global(img){
-webkit-user-drag: none;
}
/* sidebar patches */
.page-wrapper.with-sidebar[data-sidebar-hidden] {
--sidebar-width: var(--sidebar-minimised);
}
:global(::-webkit-inner-spin-button) {
opacity: 1;
margin-left: 0.4rem;
margin-right: -0.5rem;
filter: invert(0.84);
padding-top: 2rem;
}
@media (max-width: 768px) {
.page-wrapper.with-sidebar[data-sidebar-type~='overlayed-sm-and-down'] > :global(.content-wrapper) {

View file

@ -1,159 +0,0 @@
<script>
import { countdown } from '../modules/util.js'
import { getContext } from 'svelte'
export let cards = new Promise(() => {})
const view = getContext('view')
function viewMedia(media) {
$view = media
}
</script>
{#await cards}
{#each Array(5) as _}
<div class="card m-0 p-0">
<div class="row h-full">
<div class="col-4 skeloader" />
<div class="col-8 bg-very-dark px-15 py-10">
<p class="skeloader w-300 h-25 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
</div>
</div>
</div>
{/each}
{:then cards}
{#each cards as card}
{#if !card.media}
<div class="card m-0 p-0" on:click={card.onclick}>
<div class="row h-full">
<div class="col-4 skeloader" />
<div class="col-8 bg-very-dark px-15 py-10">
<h5 class="m-0 text-capitalize font-weight-bold pb-10">{[card.parseObject.anime_title, card.parseObject.episode_number].filter(s => s).join(' - ')}</h5>
<p class="skeloader w-150 h-10 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
</div>
</div>
</div>
{:else}
<div class="card m-0 p-0" on:click={card.onclick || (() => viewMedia(card.media))} style:--color={card.media.coverImage.color || '#1890ff'}>
<div class="row h-full">
<div class="col-4">
<img loading="lazy" src={card.media.coverImage.extraLarge || ''} alt="cover" class="cover-img w-full h-full" />
</div>
<div class="col-8 h-full card-grid">
<div class="px-15 py-10 bg-very-dark">
<h5 class="m-0 text-capitalize font-weight-bold">{[card.media.title.userPreferred, card.episodeNumber].filter(s => s).join(' - ')}</h5>
{#if card.schedule && card.media.nextAiringEpisode}
<span class="text-muted font-weight-bold">
{'EP ' + card.media.nextAiringEpisode.episode + ' in ' + countdown(card.media.nextAiringEpisode.timeUntilAiring)}
</span>
{/if}
<p class="text-muted m-0 text-capitalize details">
{#if card.media.format === 'TV'}
<span>TV Show</span>
{:else if card.media.format}
<span>{card.media.format?.toLowerCase().replace(/_/g, ' ')}</span>
{/if}
{#if card.media.episodes}
<span>{card.media.episodes + ' Episodes'}</span>
{:else if card.media.duration}
<span>{card.media.duration + ' Minutes'}</span>
{/if}
{#if card.media.status}
<span>{card.media.status?.toLowerCase().replace(/_/g, ' ')}</span>
{/if}
<span>
{[card.media.season?.toLowerCase(), card.media.seasonYear].filter(s => s).join(' ')}
</span>
</p>
</div>
<div class="overflow-y-auto px-15 pb-5 bg-very-dark card-desc">
{@html card.media.description}
</div>
<div class="px-15 pb-10 pt-5 genres">
{#each card.media.genres as genre}
<span class="badge badge-pill badge-color text-dark mt-5 font-weight-bold">{genre}</span>
{/each}
</div>
</div>
</div>
</div>
{/if}
{/each}
{/await}
<style>
.h-10 {
height: 1rem !important;
}
.card-desc :global(p) {
margin: 0;
}
.details span + span::before {
content: ' • ';
}
.card {
will-change: transform, bottom;
animation: 0.3s ease 0s 1 load-in;
cursor: pointer;
overflow: hidden;
transition: transform 0.2s ease;
height: 27rem !important;
box-shadow: rgba(0, 4, 12, 0.3) 0px 7px 15px, rgba(0, 4, 12, 0.05) 0px 4px 4px;
}
.card-grid {
display: grid;
grid-template-rows: auto 1fr auto;
}
.badge-color {
background-color: var(--color) !important;
border-color: var(--color) !important;
}
.card:hover {
transform: scale(1.05);
}
@keyframes load {
from {
left: -100%;
}
to {
left: 100%;
}
}
@keyframes load-in {
from {
bottom: -1.2rem;
transform: scale(0.95);
}
to {
bottom: 0;
transform: scale(1);
}
}
.skeloader {
position: relative;
overflow: hidden;
}
.skeloader::before {
will-change: left;
content: '';
position: absolute;
height: 100%;
width: 15rem;
background: linear-gradient(to right, transparent 0%, #25282c 50%, transparent 100%);
animation: load 1s infinite cubic-bezier(0.4, 0, 0.2, 1);
}
.cover-img {
object-fit: cover;
background-color: var(--color) !important;
}
</style>

View file

@ -3,7 +3,6 @@
import { countdown } from '@/modules/util.js'
let view = getContext('view')
$: console.log($view)
function close() {
$view = null
}
@ -108,7 +107,7 @@
<div class="d-flex flex-column flex-wrap">
<button class="btn btn-primary d-flex align-items-center font-weight-bold font-size-24 h-50 mb-5" type="button">
<span class="material-icons mr-10 font-size-24 w-30"> play_arrow </span>
<span>Play</span>
<span>{$view.progress ? 'Continue' : 'Play'}</span>
</button>
<button class="btn d-flex align-items-center mb-5 font-weight-bold font-size-16">
<span class="material-icons mr-10 font-size-18 w-30"> live_tv </span>
@ -128,16 +127,7 @@
<div class="font-size-16 pr-15">
{@html $view.description}
</div>
<!-- <h1 class="title font-weight-bold text-white pt-20">Episodes</h1>
<div class="d-flex flex-wrap justify-content-start"> -->
<!-- <div class="position-relative w-250 rounded mr-10 mb-10 overflow-hidden pointer">
<img loading="lazy"
src="https://img1.ak.crunchyroll.com/i/spire1-tmb/b199406edeebc19a7f4e4412d6e1dfcc1365964779_full.jpg"
class="w-full h-full">
<div class="position-absolute ep-title w-full p-5 text-truncate bottom-0">Episode 1 - To You, 2,000
Years in the Future -The Fall of Zhiganshina (1)</div>
</div> -->
<!-- </div> -->
<!-- TODO: episodes list -->
</div>
<div class="col-md-3 px-sm-0 px-20">
<h1 class="title font-weight-bold text-white">Details</h1>

View file

@ -0,0 +1,161 @@
<script>
import { countdown } from '@/modules/util.js'
import { getContext } from 'svelte'
export let cards = new Promise(() => {})
const view = getContext('view')
function viewMedia(media) {
$view = media
}
</script>
{#await cards}
{#each Array(5) as _}
<div class="card m-0 p-0">
<div class="row h-full">
<div class="col-4 skeloader" />
<div class="col-8 bg-very-dark px-15 py-10">
<p class="skeloader w-300 h-25 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
</div>
</div>
</div>
{/each}
{:then cards}
{#each cards as card}
{#key card}
{#if !card.media}
<div class="card m-0 p-0" on:click={card.onclick}>
<div class="row h-full">
<div class="col-4 skeloader" />
<div class="col-8 bg-very-dark px-15 py-10">
<h5 class="m-0 text-capitalize font-weight-bold pb-10">{[card.parseObject.anime_title, card.parseObject.episode_number].filter(s => s).join(' - ')}</h5>
<p class="skeloader w-150 h-10 rounded bg-dark" />
<p class="skeloader w-150 h-10 rounded bg-dark" />
</div>
</div>
</div>
{:else}
<div class="card m-0 p-0" on:click={card.onclick || (() => viewMedia(card.media))} style:--color={card.media.coverImage.color || '#1890ff'}>
<div class="row h-full">
<div class="col-4">
<img loading="lazy" src={card.media.coverImage.extraLarge || ''} alt="cover" class="cover-img w-full h-full" />
</div>
<div class="col-8 h-full card-grid">
<div class="px-15 py-10 bg-very-dark">
<h5 class="m-0 text-capitalize font-weight-bold">{[card.media.title.userPreferred, card.episodeNumber].filter(s => s).join(' - ')}</h5>
{#if card.schedule && card.media.nextAiringEpisode}
<span class="text-muted font-weight-bold">
{'EP ' + card.media.nextAiringEpisode.episode + ' in ' + countdown(card.media.nextAiringEpisode.timeUntilAiring)}
</span>
{/if}
<p class="text-muted m-0 text-capitalize details">
{#if card.media.format === 'TV'}
<span>TV Show</span>
{:else if card.media.format}
<span>{card.media.format?.toLowerCase().replace(/_/g, ' ')}</span>
{/if}
{#if card.media.episodes}
<span>{card.media.episodes + ' Episodes'}</span>
{:else if card.media.duration}
<span>{card.media.duration + ' Minutes'}</span>
{/if}
{#if card.media.status}
<span>{card.media.status?.toLowerCase().replace(/_/g, ' ')}</span>
{/if}
<span>
{[card.media.season?.toLowerCase(), card.media.seasonYear].filter(s => s).join(' ')}
</span>
</p>
</div>
<div class="overflow-y-auto px-15 pb-5 bg-very-dark card-desc">
{@html card.media.description}
</div>
<div class="px-15 pb-10 pt-5 genres">
{#each card.media.genres as genre}
<span class="badge badge-pill badge-color text-dark mt-5 mr-5 font-weight-bold">{genre}</span>
{/each}
</div>
</div>
</div>
</div>
{/if}
{/key}
{/each}
{/await}
<style>
.h-10 {
height: 1rem !important;
}
.card-desc :global(p) {
margin: 0;
}
.details span + span::before {
content: ' • ';
}
.card {
will-change: transform, bottom;
animation: 0.3s ease 0s 1 load-in;
cursor: pointer;
overflow: hidden;
transition: transform 0.2s ease;
height: 27rem !important;
box-shadow: rgba(0, 4, 12, 0.3) 0px 7px 15px, rgba(0, 4, 12, 0.05) 0px 4px 4px;
}
.card-grid {
display: grid;
grid-template-rows: auto 1fr auto;
}
.badge-color {
background-color: var(--color) !important;
border-color: var(--color) !important;
}
.card:hover {
transform: scale(1.05);
}
@keyframes load {
from {
left: -100%;
}
to {
left: 100%;
}
}
@keyframes load-in {
from {
bottom: -1.2rem;
transform: scale(0.95);
}
to {
bottom: 0;
transform: scale(1);
}
}
.skeloader {
position: relative;
overflow: hidden;
}
.skeloader::before {
will-change: left;
content: '';
position: absolute;
height: 100%;
width: 15rem;
background: linear-gradient(to right, transparent 0%, #25282c 50%, transparent 100%);
animation: load 1s infinite cubic-bezier(0.4, 0, 0.2, 1);
}
.cover-img {
object-fit: cover;
background-color: var(--color) !important;
}
</style>

View file

@ -1,5 +1,5 @@
<script>
import Cards from '@/lib/Cards.svelte'
import Cards from './Cards.svelte'
// TODO: infinite scrolling

View file

@ -2,6 +2,7 @@
import Search from './Search.svelte'
import Section from './Section.svelte'
import Gallery from './Gallery.svelte'
import { add } from '@/modules/torrent.js'
import { alToken } from '../Settings.svelte'
import { alRequest } from '@/modules/anilist.js'
import { resolveFileMedia, relations } from '@/modules/anime.js'
@ -30,7 +31,10 @@
const items = doc.querySelectorAll('item')
const media = await resolveFileMedia({ fileName: [...items].map(item => item.querySelector('title').textContent).slice(0, limit), isRelease: true })
media.forEach((mediaInformation, index) => {
mediaInformation.onclick = () => client.playTorrent(items[index].querySelector('link').textContent, { media: mediaInformation, episode: mediaInformation.episode })
mediaInformation.onclick = () => {
add(items[index].querySelector('link').textContent)
// { media: mediaInformation, episode: mediaInformation.episode }
}
})
localStorage.setItem('relations', JSON.stringify(relations))
return media

View file

@ -1,5 +1,5 @@
<script>
import Cards from '@/lib/Cards.svelte'
import Cards from './Cards.svelte'
export let opts = {}
</script>

View file

@ -1,5 +1,5 @@
/* global halfmoon */
import { client } from './torrent.js'
import { add } from './torrent.js'
import { DOMPARSER } from './util.js'
import { alRequest } from './anilist.js'
import { nyaaRss } from './rss.js'
@ -18,7 +18,7 @@ window.addEventListener('paste', async e => { // WAIT image lookup on paste, or
item.getAsString(text => {
if (torrentRx.exec(text)) {
e.preventDefault()
client.playTorrent(text)
add(text)
} else if (imageRx.exec(text)) {
e.preventDefault()
traceAnime(text)

View file

@ -1,12 +1,9 @@
/* global halfmoon */
// import { settings } from './settings.js'
import { set } from '@/lib/pages/Settings.svelte'
import { DOMPARSER } from './util.js'
import { client } from './torrent.js'
import { add } from './torrent.js'
import { episodeRx } from './anime.js'
const settings = {}
// TODO: settings
const settings = set
export function getRSSContent (url) {
return fetch(url).then(res => {
@ -17,12 +14,12 @@ export function getRSSContent (url) {
}
throw Error(res.statusText)
}).catch(error => {
halfmoon.initStickyAlert({
content: 'Failed fetching RSS!<br>' + error,
title: 'Search Failed',
alertType: 'alert-danger',
fillType: ''
})
// halfmoon.initStickyAlert({
// content: 'Failed fetching RSS!<br>' + error,
// title: 'Search Failed',
// alertType: 'alert-danger',
// fillType: ''
// })
console.error(error)
})
}
@ -62,12 +59,8 @@ export async function nyaaRss (media, episode, isOffline) {
media: media
}
if (settings.torrent2) {
if (isOffline) {
client.offlineDownload(entries[0].hash)
} else {
client.playTorrent(entries[0].hash, { media: fileMedia, episode: episode })
}
halfmoon.toggleModal('tsearch')
add(entries[0].hash)
// { media: fileMedia, episode: episode }
}
entries.forEach((entry, index) => {
const template = document.createElement('tr')
@ -80,12 +73,8 @@ export async function nyaaRss (media, episode, isOffline) {
<td>${entry.downloads}</td>
<td class="pointer">Play</td>`
template.onclick = () => {
if (isOffline) {
client.offlineDownload(entry.hash)
} else {
client.playTorrent(entry.hash, { media: fileMedia, episode: episode })
}
halfmoon.hideModal('tsearch')
add(entry.hash)
// { media: fileMedia, episode: episode }
}
frag.appendChild(template)
})

View file

@ -1,3 +1,31 @@
const WebTorrent = require('webtorrent')
export const client = new WebTorrent()
console.log(client)
window.client = client
// save loaded torrent for persistence
export function add (torrentID) {
if (torrentID) {
if (client.torrents.length) client.remove(client.torrents[0].infoHash)
client.add(torrentID, {
path: 'E:\\videos\\testing\\',
// destroyStoreOnDestroy: true,
announce: [
'wss://tracker.openwebtorrent.com',
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
'wss://peertube.cpy.re:443/tracker/socket'
]
})
}
}
client.on('torrent', torrent => {
console.log('ready', torrent.name)
const string = JSON.stringify(Array.from(torrent.torrentFile))
localStorage.setItem('torrent', string)
})
// load last used torrent
if (localStorage.getItem('torrent')) {
const buffer = Buffer.from(JSON.parse(localStorage.getItem('torrent')))
add(buffer)
}

View file

@ -12,6 +12,6 @@ export function countdown (s) {
return tmp.join(' ')
}
export const DOMPARSER = new DOMParser().parseFromString.bind(new DOMParser())
export const DOMPARSER = DOMParser.prototype.parseFromString.bind(new DOMParser())
export const sleep = t => new Promise(resolve => setTimeout(resolve, t))

View file

@ -11,7 +11,8 @@ const commonjsPackages = [
// 'electron/common',
// 'electron/renderer',
// 'original-fs',
'webtorrent'
'webtorrent',
'buffer'
// ...builtinModules
]