Initial dump from 'Animarr 2019-08-23.zip'
This commit is contained in:
parent
59b8413fe1
commit
a94dee7269
38
Animarr.sh
Executable file
38
Animarr.sh
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$(realpath $0)")"
|
||||
|
||||
CONFIG_FILE="${1}"
|
||||
if [[ "$CONFIG_FILE" == "" ]]; then
|
||||
CONFIG_FILE="config.ini"
|
||||
fi
|
||||
|
||||
|
||||
#php src/Animarr.php "$CONFIG_FILE" &
|
||||
#DAEMON_PID=$!
|
||||
|
||||
|
||||
MAGNET_FOLDER=$(awk -F "=" '/magnet.service.folder/ {print $2}' "$CONFIG_FILE")
|
||||
TORRENT_FOLDER=$(awk -F "=" '/torrent.cache.folder/ {print $2}' "$CONFIG_FILE")
|
||||
|
||||
nodejs src/MagnetService.js "${MAGNET_FOLDER}" "${TORRENT_FOLDER}" &
|
||||
MAGNET_PID=$!
|
||||
|
||||
SERVER_HOST=$(awk -F "=" '/server.host/ {print $2}' "$CONFIG_FILE")
|
||||
SERVER_PORT=$(awk -F "=" '/server.port/ {print $2}' "$CONFIG_FILE")
|
||||
|
||||
function finish {
|
||||
#kill $DAEMON_PID
|
||||
kill $MAGNET_PID
|
||||
}
|
||||
trap finish EXIT
|
||||
|
||||
rm "config_${SERVER_PORT}.ini"
|
||||
|
||||
ln -s "$CONFIG_FILE" "config_${SERVER_PORT}.ini"
|
||||
|
||||
php src/Animarr.php "$CONFIG_FILE"
|
||||
|
||||
#php -S "$SERVER_HOST:$SERVER_PORT" -t web/
|
||||
|
||||
|
14
INSTALL
Normal file
14
INSTALL
Normal file
|
@ -0,0 +1,14 @@
|
|||
Requires >= PHP 7.2 and postgres >= 9.4. PHP-FPM/nginx recommended if you want the web interface.
|
||||
|
||||
Some of these packages can be installed via:
|
||||
apt install php-cli php-pgsql php-curl php-mbstring postgresql postgresql-client
|
||||
|
||||
You will have to create and use the db.sql schema to create what animarr needs.
|
||||
|
||||
Optional web dependencies can be installed this way:
|
||||
apt install nginx php-fpm
|
||||
|
||||
If you want magnet fetching, it uses nodejs with some other stuff. Figure that out.
|
||||
|
||||
Example config files and scripts are setup to use the /opt/animarr/Animarr install path for this code. Modify at your discretion.
|
||||
These have comments in them to help you on the way
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2019 DataHoarder
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
221
config.ini
Executable file
221
config.ini
Executable file
|
@ -0,0 +1,221 @@
|
|||
#Config file for Animarr
|
||||
|
||||
#Force a global search of everything tracked, clearing the downloaded episodes information
|
||||
#search.force=true
|
||||
#search.forceclear=true
|
||||
#Force a global refresh of everything tracked, using information in the database. Recommended when settings change!
|
||||
#refresh.force=true
|
||||
#search.forcenew=true
|
||||
|
||||
server.host=0.0.0.0
|
||||
server.port=8083
|
||||
|
||||
#Default download folder
|
||||
download.folder=/CHANGE_THIS_PATH_TO_YOUR_OWN/Anime/Series
|
||||
|
||||
magnet.service.enable=true
|
||||
magnet.service.folder=/opt/animarr/magnets
|
||||
magnet.service.command.timeout=600
|
||||
magnet.unknownRelease.wait=true
|
||||
torrent.cache.folder=/opt/animarr/torrents
|
||||
#This setting will add failed torrents as empty torrents for URLs, or read them
|
||||
torrent.cache.addEmpty=true
|
||||
|
||||
#Automatically match new downloads and track those shows!
|
||||
post.autotrack=false
|
||||
#Only copy episodes matched as the current version
|
||||
post.matchrelease=true
|
||||
#Delete old episodes found in download folder
|
||||
post.deleteold=true
|
||||
#Delete failed episodes (mismatch, bad CRC, etc.)
|
||||
post.deletefailed=true
|
||||
|
||||
post.specials=true
|
||||
post.verifycrc=true
|
||||
|
||||
torrent.folder.blackhole=/CHANGE_THIS_PATH_TO_YOUR_OWN/AutoDL/add/Anime
|
||||
torrent.folder.finished=/CHANGE_THIS_PATH_TO_YOUR_OWN/AutoDL/finished/Anime
|
||||
|
||||
torrent.folder.private.blackhole=/CHANGE_THIS_PATH_TO_YOUR_OWN/AutoDL/add/AnimeSeeding
|
||||
torrent.folder.private.finished=/CHANGE_THIS_PATH_TO_YOUR_OWN/AutoDL/finished/AnimeSeeding
|
||||
|
||||
tracker.domain.private[]=animebytes.tv
|
||||
tracker.domain.private[]=bakabt.me
|
||||
#Seconds to wait between fetching torrents
|
||||
tracker.private.fetchDelay=1
|
||||
|
||||
log.path=/opt/animarr/animarr.log
|
||||
database.anidb=data/anidb.cache.dat
|
||||
database.anidb.extra=data/anidb.extra.dat
|
||||
#include config/exceptions.ini
|
||||
#database.folder=/opt/animarr/database
|
||||
database.psql.user=animarr
|
||||
database.psql.password=YOUR_POSTGRES_PASSWORD
|
||||
database.psql.host=127.0.0.1
|
||||
database.psql.dbname=animarr
|
||||
database.api.folder=/opt/animarr/database/api
|
||||
#Force cleanup of old episodes/releases on database
|
||||
database.forceclean=true
|
||||
|
||||
interval.poll.tracker=10
|
||||
interval.poll.finished=5
|
||||
interval.poll.new=300
|
||||
|
||||
#Will send a webhook for every episode added
|
||||
#webhook.episode.added[]=https://test.com/your.hook?extra
|
||||
|
||||
|
||||
#AnimeBytes
|
||||
tracker.animebytes.enable=true
|
||||
#CATEGORY_RAW, CATEGORY_SOFTSUBS, CATEGORY_HARDSUBS, CATEGORY_SUBS, CATEGORY_ALL
|
||||
tracker.animebytes.category=CATEGORY_SUBS
|
||||
tracker.animebytes.scrapeUrl=https://animebytes.tv/scrape.php
|
||||
tracker.animebytes.auth.username=<YOUR_AB_USERNAME>
|
||||
tracker.animebytes.auth.passkeyFile=keys/AnimeBytes.key
|
||||
tracker.animebytes.goldlist.trusted=true
|
||||
tracker.animebytes.trusted[HorribleSubs]=true
|
||||
|
||||
#NyaaTorrents (OLD, DISABLED)
|
||||
tracker.nyaa.enable=false
|
||||
#FILTER_ALL, FILTER_REMAKES, FILTER_TRUSTED_ONLY, FILTER_APLUS_ONLY
|
||||
tracker.nyaa.filter=FILTER_REMAKES
|
||||
#CATEGORY_ANIME_RAW, CATEGORY_ANIME_ENGLISH, CATEGORY_ANIME_NON_ENGLISH
|
||||
tracker.nyaa.category=CATEGORY_ANIME_ENGLISH
|
||||
tracker.nyaa.rss.baseurl=https://www.nyaa.se/?page=rss
|
||||
tracker.nyaa.unknown.source[HorribleSubs]=WEB
|
||||
tracker.nyaa.unknown.source[CR]=WEB
|
||||
tracker.nyaa.unknown.source[CReep]=WEB
|
||||
tracker.nyaa.unknown.source.default=TV
|
||||
tracker.nyaa.unknown.quality.default=720
|
||||
tracker.nyaa.unknown.videoCodec.default=H264
|
||||
|
||||
#NyaaSi
|
||||
tracker.nyaasi.enable=true
|
||||
#FILTER_ALL, FILTER_REMAKES, FILTER_TRUSTED_ONLY, FILTER_APLUS_ONLY
|
||||
tracker.nyaasi.filter=FILTER_REMAKES
|
||||
#CATEGORY_ANIME_RAW, CATEGORY_ANIME_ENGLISH, CATEGORY_ANIME_NON_ENGLISH
|
||||
tracker.nyaasi.category=CATEGORY_ANIME_ENGLISH
|
||||
tracker.nyaasi.rss.baseurl=https://nyaa.si/?page=rss
|
||||
tracker.nyaasi.api.baseurl=https://nyaa.si/api/
|
||||
tracker.nyaasi.unknown.source[HorribleSubs]=WEB
|
||||
tracker.nyaasi.unknown.source[CR]=WEB
|
||||
tracker.nyaasi.unknown.source[CReep]=WEB
|
||||
tracker.nyaasi.unknown.source.default=TV
|
||||
tracker.nyaasi.unknown.quality.default=720
|
||||
tracker.nyaasi.unknown.videoCodec.default=H264
|
||||
tracker.nyaasi.goldlist.trusted=true
|
||||
tracker.nyaasi.trusted[Chihiro]=true
|
||||
tracker.nyaasi.trusted[Tsundere]=true
|
||||
tracker.nyaasi.trusted[HorribleSubs]=true
|
||||
tracker.nyaasi.trusted[SallySubs]=true
|
||||
|
||||
#AniDex.info
|
||||
tracker.anidex.enable=true
|
||||
#SELECTOR_BATCH, SELECTOR_RAW, SELECTOR_REMAKES. SELECTOR_DEFAULT = BATCH + REMAKES. Depending on filterMode, it will include singles
|
||||
tracker.anidex.selector=SELECTOR_BATCH
|
||||
#LANGUAGE_ALL, LANGUAGE_ENGLISH
|
||||
tracker.anidex.language=LANGUAGE_ENGLISH
|
||||
#FILTER_MODE_INCLUDE, FILTER_MODE_ONLY
|
||||
tracker.anidex.filterMode=FILTER_MODE_INCLUDE
|
||||
tracker.anidex.ajax.baseurl=https://anidex.info/ajax/page.ajax.php?page=torrents
|
||||
tracker.anidex.unknown.source.default=TV
|
||||
tracker.anidex.unknown.quality.default=720
|
||||
tracker.anidex.unknown.videoCodec.default=H264
|
||||
tracker.anidex.trusted[Doki]=true
|
||||
tracker.anidex.trusted[Chihiro]=true
|
||||
tracker.anidex.trusted[Tsundere]=true
|
||||
tracker.anidex.trusted[HorribleSubs]=true
|
||||
tracker.anidex.trusted[SallySubs]=true
|
||||
tracker.anidex.unknown.source[HorribleSubs]=WEB
|
||||
tracker.anidex.goldlist.trusted=true
|
||||
|
||||
#HorribleSubs RSS feed with magnet links
|
||||
tracker.hs.enable=true
|
||||
tracker.hs.rss.baseurl=https://horriblesubs.info/rss.php?res=all
|
||||
tracker.hs.unknown.source[HorribleSubs]=WEB
|
||||
tracker.hs.unknown.source[CR]=WEB
|
||||
tracker.hs.unknown.source[CReep]=WEB
|
||||
tracker.hs.unknown.source.default=WEB
|
||||
tracker.hs.unknown.quality.default=720
|
||||
tracker.hs.unknown.videoCodec.default=H264
|
||||
|
||||
#HorribleSubs Base Feed
|
||||
tracker.hsold.enable=true
|
||||
tracker.hsold.feed.baseurl=https://horriblesubs.info/lib/latest.php?mode=batch
|
||||
tracker.hsold.unknown.source[HorribleSubs]=WEB
|
||||
tracker.hsold.unknown.source[CR]=WEB
|
||||
tracker.hsold.unknown.source[CReep]=WEB
|
||||
tracker.hsold.unknown.source.default=WEB
|
||||
tracker.hsold.unknown.quality.default=720
|
||||
tracker.hsold.unknown.videoCodec.default=H264
|
||||
|
||||
#include config/webfeeds.ini
|
||||
|
||||
anidb.titles.url=http://anidb.net/api/anime-titles.dat.gz
|
||||
anidb.query.languages[]=en
|
||||
anidb.query.languages[]=x-jat
|
||||
anidb.query.minlength=4
|
||||
|
||||
|
||||
#Poll that is done at start, checks any missing files between starts
|
||||
tracker.startpoll.limit=300
|
||||
|
||||
#Poll that gets run with search terms for new shows
|
||||
tracker.firstpoll.limit=-1
|
||||
|
||||
#Poll that gets run every X minutes while the program runs
|
||||
tracker.poll.limit=0
|
||||
|
||||
#Minimum number of seeds that torrents need.
|
||||
filter.minseeds=0
|
||||
#Number of seconds before current time where seed number will be ignored
|
||||
filter.newseed.time=2400
|
||||
#Whether to allow dubs or not
|
||||
filter.dubs=true
|
||||
#Remove invalid download URLs from releases
|
||||
filter.invalidDownload=true
|
||||
#Remove other random stuff
|
||||
filter.keyword.blacklist[]=vostfr
|
||||
|
||||
#Ignore these file extensions, if found
|
||||
filter.extension.ignore[]=rar
|
||||
filter.extension.ignore[]=zip
|
||||
filter.extension.ignore[]=7z
|
||||
filter.extension.ignore[]=ass
|
||||
filter.extension.ignore[]=ssa
|
||||
filter.extension.ignore[]=srt
|
||||
filter.extension.ignore[]=sfv
|
||||
filter.extension.ignore[]=xdelta
|
||||
filter.extension.ignore[]=flac
|
||||
filter.extension.ignore[]=m4a
|
||||
filter.extension.ignore[]=mp3
|
||||
filter.extension.ignore[]=aac
|
||||
filter.extension.ignore[]=mds
|
||||
filter.extension.ignore[]=iso
|
||||
filter.extension.ignore[]=vob
|
||||
filter.extension.ignore[]=flv
|
||||
filter.extension.ignore[]=wmv
|
||||
filter.extension.ignore[]=ts
|
||||
filter.extension.ignore[]=m2ts
|
||||
filter.extension.ignore[]=dvd5
|
||||
filter.extension.ignore[]=dvd9
|
||||
|
||||
filter.extension.greylist[]=avi
|
||||
filter.extension.greylist[]=mp4
|
||||
filter.extension.greylist[]=ogm
|
||||
filter.extension.greylist[]=wmv
|
||||
filter.extension.greylist[]=mpg
|
||||
filter.extension.greylist[]=mpeg
|
||||
|
||||
|
||||
#include config/groupsGoldlist.ini
|
||||
|
||||
#include config/groupsGreylist.ini
|
||||
|
||||
#include config/groupsBlacklist.ini
|
||||
|
||||
#include config/priority.ini
|
||||
|
||||
#include config/pedanticPriority.ini
|
||||
|
||||
#include config/specials.ini
|
9
config/exceptions.ini
Normal file
9
config/exceptions.ini
Normal file
|
@ -0,0 +1,9 @@
|
|||
anidb.exceptions[]={"aid": 98, "group": "HorribleSubs", "title": "Berserk", "newAid": 11851}
|
||||
anidb.exceptions[]={"aid": 12942, "group": "HorribleSubs", "title": "Mahou Tsukai no Yome", "newAid": 11982}
|
||||
anidb.exceptions[]={"aid": 12351, "group": "HorribleSubs", "title": "Black Clover", "newAid": 12665}
|
||||
anidb.exceptions[]={"aid": 12414, "newAid": 11851}
|
||||
anidb.exceptions[]={"aid": 9983, "group": "HorribleSubs", "title": "Houseki no Kuni", "newAid": 13165}
|
||||
anidb.exceptions[]={"aid": 13435, "group": "HorribleSubs", "title": "Hinamatsuri", "newAid": 13434}
|
||||
anidb.exceptions[]={"aid": 13435, "group": "Erai-raws", "title": "Hinamatsuri", "newAid": 13434}
|
||||
anidb.exceptions[]={"aid": 13435, "group": "Vivid", "title": "Hinamatsuri", "newAid": 13434}
|
||||
anidb.exceptions[]={"aid": 13435, "group": "RH", "title": "Hinamatsuri", "newAid": 13434}
|
6
config/feeds/Asenshi.ini
Normal file
6
config/feeds/Asenshi.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Asenshi]=https://asenshi.moe/feed/
|
||||
tracker.webfeed.Asenshi.unknown.group=Asenshi
|
||||
tracker.webfeed.Asenshi.trusted[Asenshi]=true
|
||||
tracker.webfeed.Asenshi.unknown.source.default=TV
|
||||
tracker.webfeed.Asenshi.unknown.quality.default=720
|
||||
tracker.webfeed.Asenshi.unknown.videoCodec.default=H264
|
8
config/feeds/Chihiro.ini
Normal file
8
config/feeds/Chihiro.ini
Normal file
|
@ -0,0 +1,8 @@
|
|||
tracker.webfeed[Chihiro]=https://chihiro-subs.com/feed/
|
||||
#tracker.webfeed.Chihiro.pagination=false
|
||||
#tracker.webfeed.Chihiro.modifyTitle=false
|
||||
tracker.webfeed.Chihiro.unknown.group=Chihiro
|
||||
tracker.webfeed.Chihiro.trusted[Chihiro]=true
|
||||
tracker.webfeed.Chihiro.unknown.source.default=TV
|
||||
tracker.webfeed.Chihiro.unknown.quality.default=720
|
||||
tracker.webfeed.Chihiro.unknown.videoCodec.default=H264
|
6
config/feeds/Coalgirls.ini
Normal file
6
config/feeds/Coalgirls.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Coalgirls]=https://coalgirls.wakku.to/feed/
|
||||
tracker.webfeed.Coalgirls.unknown.group=Coalgirls
|
||||
tracker.webfeed.Coalgirls.trusted[Coalgirls]=true
|
||||
tracker.webfeed.Coalgirls.unknown.source.default=TV
|
||||
tracker.webfeed.Coalgirls.unknown.quality.default=720
|
||||
tracker.webfeed.Coalgirls.unknown.videoCodec.default=H264
|
6
config/feeds/Commie.ini
Normal file
6
config/feeds/Commie.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Commie]=https://commiesubs.com/feed/
|
||||
tracker.webfeed.Commie.unknown.group=Commie
|
||||
tracker.webfeed.Commie.trusted[Commie]=true
|
||||
tracker.webfeed.Commie.unknown.source.default=TV
|
||||
tracker.webfeed.Commie.unknown.quality.default=720
|
||||
tracker.webfeed.Commie.unknown.videoCodec.default=H264
|
9
config/feeds/DameDesuYo.ini
Normal file
9
config/feeds/DameDesuYo.ini
Normal file
|
@ -0,0 +1,9 @@
|
|||
tracker.webfeed[DameDesuYo]=https://damedesuyo.com/?feed=rss2
|
||||
tracker.webfeed.DameDesuYo.unknown.group=DameDesuYo
|
||||
tracker.webfeed.DameDesuYo.trusted[DameDesuYo]=true
|
||||
tracker.webfeed.DameDesuYo.trusted[BlurayDesuYo]=true
|
||||
tracker.webfeed.DameDesuYo.trusted[GJM-DDY]=true
|
||||
tracker.webfeed.DameDesuYo.trusted[DDY]=true
|
||||
tracker.webfeed.DameDesuYo.unknown.source.default=TV
|
||||
tracker.webfeed.DameDesuYo.unknown.quality.default=720
|
||||
tracker.webfeed.DameDesuYo.unknown.videoCodec.default=H264
|
7
config/feeds/FFF.ini
Executable file
7
config/feeds/FFF.ini
Executable file
|
@ -0,0 +1,7 @@
|
|||
tracker.webfeed[FFF]=http://fffansubs.org/?feed=rss2
|
||||
tracker.webfeed.FFF.unknown.group=FFF
|
||||
tracker.webfeed.FFF.trusted[FFF]=true
|
||||
tracker.webfeed.FFF.trusted[FFFpeeps]=true
|
||||
tracker.webfeed.FFF.unknown.source.default=TV
|
||||
tracker.webfeed.FFF.unknown.quality.default=720
|
||||
tracker.webfeed.FFF.unknown.videoCodec.default=H264
|
4
config/feeds/GANGO.ini
Normal file
4
config/feeds/GANGO.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
tracker.webfeed[GANGO]=https://goodanimenevergetsold.wordpress.com/feed/
|
||||
tracker.webfeed.GANGO.unknown.group=GANGO
|
||||
tracker.webfeed.GANGO.trusted[GANGO]=true
|
||||
tracker.webfeed.GANGO.unknown.videoCodec.default=H264
|
7
config/feeds/GoodJobMedia.ini
Normal file
7
config/feeds/GoodJobMedia.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
tracker.webfeed[GoodJobMedia]=https://www.goodjobmedia.com/feed/
|
||||
tracker.webfeed.GoodJobMedia.unknown.group=GJM
|
||||
tracker.webfeed.GoodJobMedia.trusted[GJM]=true
|
||||
tracker.webfeed.GoodJobMedia.trusted[GJM-DDY]=true
|
||||
tracker.webfeed.GoodJobMedia.unknown.source.default=TV
|
||||
tracker.webfeed.GoodJobMedia.unknown.quality.default=720
|
||||
tracker.webfeed.GoodJobMedia.unknown.videoCodec.default=H264
|
6
config/feeds/Harunatsu.ini
Normal file
6
config/feeds/Harunatsu.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Harunatsu]=https://harunatsufansubs.com/feed/
|
||||
tracker.webfeed.Harunatsu.unknown.group=Harunatsu
|
||||
tracker.webfeed.Harunatsu.trusted[Harunatsu]=true
|
||||
tracker.webfeed.Harunatsu.unknown.source.default=TV
|
||||
tracker.webfeed.Harunatsu.unknown.quality.default=720
|
||||
tracker.webfeed.Harunatsu.unknown.videoCodec.default=H264
|
7
config/feeds/Hatsuyuki.ini
Normal file
7
config/feeds/Hatsuyuki.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
tracker.webfeed[Hatsuyuki]=https://hatsuyuki-fansubs.com/?format=feed&type=rss
|
||||
tracker.webfeed.Hatsuyuki.pagination=false
|
||||
tracker.webfeed.Hatsuyuki.unknown.group=Hatsuyuki
|
||||
tracker.webfeed.Hatsuyuki.trusted[Hatsuyuki]=true
|
||||
tracker.webfeed.Hatsuyuki.unknown.source.default=TV
|
||||
tracker.webfeed.Hatsuyuki.unknown.quality.default=720
|
||||
tracker.webfeed.Hatsuyuki.unknown.videoCodec.default=H264
|
6
config/feeds/Hiryuu.ini
Normal file
6
config/feeds/Hiryuu.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Hiryuu]=http://www.hiryuufansubs.com/feed/
|
||||
tracker.webfeed.Hiryuu.unknown.group=Hiryuu
|
||||
tracker.webfeed.Hiryuu.trusted[Hiryuu]=true
|
||||
tracker.webfeed.DameDesuYo.unknown.source.default=TV
|
||||
tracker.webfeed.DameDesuYo.unknown.quality.default=720
|
||||
tracker.webfeed.DameDesuYo.unknown.videoCodec.default=H264
|
7
config/feeds/HoshiNoYume.ini
Normal file
7
config/feeds/HoshiNoYume.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
tracker.webfeed[HoshiNoYume]=https://hoshinoyumefs.wordpress.com/feed/
|
||||
tracker.webfeed.HoshiNoYume.unknown.group=HnY
|
||||
tracker.webfeed.HoshiNoYume.trusted[HnY]=true
|
||||
tracker.webfeed.HoshiNoYume.trusted[HoshiNoYume]=true
|
||||
tracker.webfeed.HoshiNoYume.unknown.source.default=DVD
|
||||
tracker.webfeed.HoshiNoYume.unknown.quality.default=480
|
||||
tracker.webfeed.HoshiNoYume.unknown.videoCodec.default=H264_10
|
6
config/feeds/Kaitou.ini
Executable file
6
config/feeds/Kaitou.ini
Executable file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Kaitou]=https://kaitou-fansubs.com/?feed=rss2
|
||||
tracker.webfeed.Kaitou.unknown.group=Kaitou
|
||||
tracker.webfeed.Kaitou.trusted[Kaitou]=true
|
||||
tracker.webfeed.Kaitou.unknown.source.default=TV
|
||||
tracker.webfeed.Kaitou.unknown.quality.default=720
|
||||
tracker.webfeed.Kaitou.unknown.videoCodec.default=H264
|
6
config/feeds/Kametsu.ini
Executable file
6
config/feeds/Kametsu.ini
Executable file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Kametsu]=https://projects.kametsu.com/feed
|
||||
tracker.webfeed.Kametsu.unknown.group=Kametsu
|
||||
tracker.webfeed.Kametsu.trusted[Kametsu]=true
|
||||
tracker.webfeed.Kametsu.unknown.source.default=BLURAY
|
||||
tracker.webfeed.Kametsu.unknown.quality.default=720
|
||||
tracker.webfeed.Kametsu.unknown.videoCodec.default=H264
|
6
config/feeds/Kantai.ini
Normal file
6
config/feeds/Kantai.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Kantai]=https://kantai.subs.moe/feed/
|
||||
tracker.webfeed.Kantai.unknown.group=Kantai
|
||||
tracker.webfeed.Kantai.trusted[Kantai]=true
|
||||
tracker.webfeed.Kantai.unknown.source.default=TV
|
||||
tracker.webfeed.Kantai.unknown.quality.default=720
|
||||
tracker.webfeed.Kantai.unknown.videoCodec.default=H264
|
6
config/feeds/Mezashite.ini
Normal file
6
config/feeds/Mezashite.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Mezashite]=http://mezashite.net/feed/
|
||||
tracker.webfeed.Mezashite.unknown.group=Mezashite
|
||||
tracker.webfeed.Mezashite.trusted[Mezashite]=true
|
||||
tracker.webfeed.Mezashite.unknown.source.default=TV
|
||||
tracker.webfeed.Mezashite.unknown.quality.default=720
|
||||
tracker.webfeed.Mezashite.unknown.videoCodec.default=H264
|
6
config/feeds/Mori.ini
Normal file
6
config/feeds/Mori.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Mori]=https://mori.subs.moe/feed/
|
||||
tracker.webfeed.Mori.unknown.group=Mori
|
||||
tracker.webfeed.Mori.trusted[Mori]=true
|
||||
tracker.webfeed.Mori.unknown.source.default=TV
|
||||
tracker.webfeed.Mori.unknown.quality.default=720
|
||||
tracker.webfeed.Mori.unknown.videoCodec.default=H264
|
6
config/feeds/PAS.ini
Normal file
6
config/feeds/PAS.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[PAS]=https://asenshi.moe/feed/
|
||||
tracker.webfeed.PAS.unknown.group=PAS
|
||||
tracker.webfeed.PAS.trusted[PAS]=true
|
||||
tracker.webfeed.PAS.unknown.source.default=TV
|
||||
tracker.webfeed.PAS.unknown.quality.default=720
|
||||
tracker.webfeed.PAS.unknown.videoCodec.default=H264
|
6
config/feeds/SallySubs.ini
Normal file
6
config/feeds/SallySubs.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[SallySubs]=https://www.sallysubs.com/feed/
|
||||
tracker.webfeed.SallySubs.unknown.group=SallySubs
|
||||
tracker.webfeed.SallySubs.trusted[SallySubs]=true
|
||||
tracker.webfeed.SallySubs.unknown.source.default=TV
|
||||
tracker.webfeed.SallySubs.unknown.quality.default=720
|
||||
tracker.webfeed.SallySubs.unknown.videoCodec.default=H264
|
6
config/feeds/SquareSubs.ini
Normal file
6
config/feeds/SquareSubs.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[SquareSubs]=https://squaresubs.blogspot.com/feeds/posts/default?alt=rss
|
||||
tracker.webfeed.SquareSubs.unknown.group=SquareSubs
|
||||
tracker.webfeed.SquareSubs.trusted[SquareSubs]=true
|
||||
tracker.webfeed.SquareSubs.unknown.source.default=DVD
|
||||
tracker.webfeed.SquareSubs.unknown.quality.default=480
|
||||
tracker.webfeed.SquareSubs.unknown.videoCodec.default=H264_10
|
6
config/feeds/Tsundere.ini
Normal file
6
config/feeds/Tsundere.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[Tsundere]=https://tsundererips.wordpress.com/feed/
|
||||
tracker.webfeed.Tsundere.unknown.group=Tsundere
|
||||
tracker.webfeed.Tsundere.trusted[Tsundere]=true
|
||||
tracker.webfeed.Tsundere.unknown.source.default=TV
|
||||
tracker.webfeed.Tsundere.unknown.quality.default=720
|
||||
tracker.webfeed.Tsundere.unknown.videoCodec.default=H264
|
7
config/feeds/UTW.ini
Normal file
7
config/feeds/UTW.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
tracker.webfeed[UTW]=https://utw.me/feed/
|
||||
tracker.webfeed.UTW.unknown.group=UTW
|
||||
tracker.webfeed.UTW.trusted[UTW]=true
|
||||
tracker.webfeed.UTW.trusted[FTW]=true
|
||||
tracker.webfeed.UTW.unknown.source.default=TV
|
||||
tracker.webfeed.UTW.unknown.quality.default=720
|
||||
tracker.webfeed.UTW.unknown.videoCodec.default=H264
|
6
config/feeds/gg.ini
Normal file
6
config/feeds/gg.ini
Normal file
|
@ -0,0 +1,6 @@
|
|||
tracker.webfeed[gg]=https://ggkthx.org/feed/
|
||||
tracker.webfeed.gg.unknown.group=gg
|
||||
tracker.webfeed.gg.trusted[gg]=true
|
||||
tracker.webfeed.gg.unknown.source.default=TV
|
||||
tracker.webfeed.gg.unknown.quality.default=720
|
||||
tracker.webfeed.gg.unknown.videoCodec.default=H264
|
31
config/groupsBlacklist.ini
Normal file
31
config/groupsBlacklist.ini
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
#Anything here will be filtered out
|
||||
filter.groups.blacklist[]=DeadFish
|
||||
filter.groups.blacklist[]=BudLightSubs
|
||||
filter.groups.blacklist[]=BakedFish
|
||||
filter.groups.blacklist[]=Mites
|
||||
filter.groups.blacklist[]=AnimeRG
|
||||
filter.groups.blacklist[]=NH
|
||||
filter.groups.blacklist[]=trash
|
||||
filter.groups.blacklist[]=CR
|
||||
filter.groups.blacklist[]=CR-1080p
|
||||
filter.groups.blacklist[]=CR-720p
|
||||
filter.groups.blacklist[]=CR-480p
|
||||
filter.groups.blacklist[]=seiya
|
||||
filter.groups.blacklist[]=CReep
|
||||
filter.groups.blacklist[]=Crunchy
|
||||
filter.groups.blacklist[]=notmaifuni
|
||||
filter.groups.blacklist[]=rubadubdub
|
||||
filter.groups.blacklist[]=FuniRip
|
||||
filter.groups.blacklist[]=DLSuki
|
||||
filter.groups.blacklist[]=Hi10
|
||||
filter.groups.blacklist[]=Buddy Waters
|
||||
filter.groups.blacklist[]=LostYears
|
||||
filter.groups.blacklist[]=PuyaSubs!
|
||||
filter.groups.blacklist[]=Golumpa
|
||||
filter.groups.blacklist[]=rubadubdubthanksforthegrub
|
||||
filter.groups.blacklist[]=Tompel Fansub
|
||||
filter.groups.blacklist[]=FreakCrSuBuS
|
||||
filter.groups.blacklist[]=GHOST
|
||||
#No group
|
||||
#filter.groups.blacklist[]=UNKNOWN
|
77
config/groupsGoldlist.ini
Normal file
77
config/groupsGoldlist.ini
Normal file
|
@ -0,0 +1,77 @@
|
|||
|
||||
#Anything here will be preferred if they have the same source, or if they have the same priority
|
||||
|
||||
|
||||
#DameDesuYo
|
||||
filter.groups.goldlist[]=BlurayDesuYo
|
||||
filter.groups.goldlist[]=DameDesuYo
|
||||
filter.groups.goldlist[]=DDY
|
||||
|
||||
filter.groups.goldlist[]=UTW
|
||||
filter.groups.goldlist[]=Chihiro
|
||||
filter.groups.goldlist[]=Coalgirls
|
||||
filter.groups.goldlist[]=FFF
|
||||
filter.groups.goldlist[]=Asenshi
|
||||
filter.groups.goldlist[]=Mezashite
|
||||
filter.groups.goldlist[]=GJM
|
||||
filter.groups.goldlist[]=CBM
|
||||
filter.groups.goldlist[]=LNS
|
||||
filter.groups.goldlist[]=Mazui
|
||||
filter.groups.goldlist[]=Anime-Koi
|
||||
filter.groups.goldlist[]=Underwater
|
||||
filter.groups.goldlist[]=WhyNot
|
||||
filter.groups.goldlist[]=Vivid
|
||||
filter.groups.goldlist[]=SallySubs
|
||||
filter.groups.goldlist[]=Elysium
|
||||
filter.groups.goldlist[]=THORA
|
||||
filter.groups.goldlist[]=KAA
|
||||
filter.groups.goldlist[]=-__-'
|
||||
filter.groups.goldlist[]=coldhell
|
||||
filter.groups.goldlist[]=Hiryuu
|
||||
filter.groups.goldlist[]=eraser
|
||||
filter.groups.goldlist[]=Doki
|
||||
filter.groups.goldlist[]=Hatsuyuki
|
||||
filter.groups.goldlist[]=Mori
|
||||
filter.groups.goldlist[]=MTBB
|
||||
filter.groups.goldlist[]=Kantai
|
||||
|
||||
|
||||
#Kametsu
|
||||
filter.groups.goldlist[]=Kametsu
|
||||
filter.groups.goldlist[]=deanzel
|
||||
filter.groups.goldlist[]=Shirσ
|
||||
|
||||
#( ͡° ͜ʖ ͡°)
|
||||
filter.groups.goldlist[]=Commie
|
||||
filter.groups.goldlist[]=gg
|
||||
filter.groups.goldlist[]=Coldgirls
|
||||
|
||||
#Collabs that do not follow normal naming
|
||||
#Underwater + UTW
|
||||
filter.groups.goldlist[]=FTW
|
||||
#UTW
|
||||
filter.groups.goldlist[]=Meguca
|
||||
filter.groups.goldlist[]=Victorique
|
||||
filter.groups.goldlist[]=Chibiki
|
||||
#FFF
|
||||
filter.groups.goldlist[]=FFFpeeps
|
||||
#coldhell
|
||||
filter.groups.goldlist[]=CH
|
||||
#Hiryuu + CTSS
|
||||
filter.groups.goldlist[]=CTSHiryuu
|
||||
|
||||
#Extra Groups
|
||||
filter.groups.goldlist[]=Dekinai
|
||||
filter.groups.goldlist[]=a-S
|
||||
filter.groups.goldlist[]=E-D
|
||||
filter.groups.goldlist[]=kuchikirukia
|
||||
filter.groups.goldlist[]=Scum
|
||||
filter.groups.goldlist[]=Foxtrot
|
||||
filter.groups.goldlist[]=Chyuu
|
||||
filter.groups.goldlist[]=PAS
|
||||
filter.groups.goldlist[]=RH
|
||||
filter.groups.goldlist[]=pyroneko
|
||||
filter.groups.goldlist[]=Kaitou
|
||||
filter.groups.goldlist[]=:v
|
||||
filter.groups.goldlist[]=ACSS
|
||||
filter.groups.goldlist[]=SCP-2223
|
18
config/groupsGreylist.ini
Normal file
18
config/groupsGreylist.ini
Normal file
|
@ -0,0 +1,18 @@
|
|||
#Anything here will be put at the bottom of the priority
|
||||
#filter.groups.greylist[]=HorribleSubs
|
||||
filter.groups.greylist[]=Merchant
|
||||
filter.groups.greylist[]=MK
|
||||
filter.groups.greylist[]=anon
|
||||
filter.groups.greylist[]=Anon
|
||||
filter.groups.greylist[]=AnonSubs
|
||||
filter.groups.greylist[]=Anonymous
|
||||
filter.groups.greylist[]=KamiFS
|
||||
filter.groups.greylist[]=An-Online
|
||||
filter.groups.greylist[]=An-Online.com
|
||||
filter.groups.greylist[]=anime4life
|
||||
filter.groups.greylist[]=Erai-raws
|
||||
filter.groups.greylist[]=Ohys-Raws
|
||||
filter.groups.greylist[]=Raws
|
||||
filter.groups.greylist[]=raws
|
||||
filter.groups.greylist[]=UNKNOWN
|
||||
|
20
config/pedanticPriority.ini
Normal file
20
config/pedanticPriority.ini
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
#Pedantic priority, used when two releases are the same priority. First one is picked, otherwise newest one. If same date, first one is picked. Keys are videoCodec, audioCodec, crc
|
||||
priority.pedantic.list[]={"videoCodec": "H264_10", "audioCodec": "FLAC", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "H264", "audioCodec": "FLAC", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "H264_10", "audioCodec": "FLAC"}
|
||||
priority.pedantic.list[]={"videoCodec": "H264", "audioCodec": "FLAC"}
|
||||
priority.pedantic.list[]={"videoCodec": "H264_10", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "H264", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "UNKNOWN", "audioCodec": "FLAC", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "UNKNOWN", "audioCodec": "FLAC"}
|
||||
priority.pedantic.list[]={"videoCodec": "UNKNOWN", "crc": true}
|
||||
priority.pedantic.list[]={"videoCodec": "H264_10"}
|
||||
priority.pedantic.list[]={"videoCodec": "H264"}
|
||||
priority.pedantic.list[]={"videoCodec": "HEVC"}
|
||||
priority.pedantic.list[]={"videoCodec": "MPEG4"}
|
||||
#Empty required to catch what is missing
|
||||
priority.pedantic.list[]={}
|
||||
|
||||
priority.pedantic.preferPublic=false
|
61
config/priority.ini
Normal file
61
config/priority.ini
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
#Priority list. Highest complete match will have priority. Keys are source, quality, best, trusted
|
||||
|
||||
#Just everything marked as best, with order to fix old duplicates
|
||||
priority.list[]={"source": "BLURAY", "quality": 1080, "best": true}
|
||||
priority.list[]={"source": "BLURAY", "quality": 720, "best": true}
|
||||
priority.list[]={"source": "BLURAY", "best": true}
|
||||
|
||||
#1080p Bluray
|
||||
priority.list[]={"source": "BLURAY", "quality": 1080, "trusted": true}
|
||||
priority.list[]={"source": "BLURAY", "quality": 1080}
|
||||
|
||||
#720p Bluray
|
||||
priority.list[]={"source": "BLURAY", "quality": 720, "trusted": true}
|
||||
priority.list[]={"source": "BLURAY", "quality": 720}
|
||||
|
||||
priority.list[]={"quality": 1080, "best": true}
|
||||
priority.list[]={"quality": 720, "best": true}
|
||||
priority.list[]={"best": true}
|
||||
|
||||
#Other Bluray
|
||||
priority.list[]={"source": "BLURAY", "trusted": true}
|
||||
priority.list[]={"source": "BLURAY"}
|
||||
|
||||
#Other DVD
|
||||
priority.list[]={"source": "DVD", "trusted": true}
|
||||
priority.list[]={"source": "DVD"}
|
||||
|
||||
#Other LASERDISC
|
||||
priority.list[]={"source": "LASERDISC", "trusted": true}
|
||||
priority.list[]={"source": "LASERDISC"}
|
||||
|
||||
#TV
|
||||
priority.list[]={"source": "TV", "quality": 1080, "trusted": true}
|
||||
priority.list[]={"source": "TV", "quality": 720, "trusted": true}
|
||||
priority.list[]={"source": "TV", "best": true}
|
||||
priority.list[]={"source": "TV", "trusted": true}
|
||||
|
||||
#WEB
|
||||
priority.list[]={"source": "WEB", "quality": 1080, "trusted": true}
|
||||
priority.list[]={"source": "WEB", "quality": 720, "trusted": true}
|
||||
priority.list[]={"source": "WEB", "trusted": true}
|
||||
priority.list[]={"source": "WEB", "quality": 1080}
|
||||
priority.list[]={"source": "WEB", "quality": 720}
|
||||
|
||||
#TV non-trusted
|
||||
priority.list[]={"source": "TV", "quality": 1080}
|
||||
priority.list[]={"source": "TV", "quality": 720}
|
||||
|
||||
#Other
|
||||
priority.list[]={"quality": 1080, "trusted": true}
|
||||
priority.list[]={"quality": 720, "trusted": true}
|
||||
priority.list[]={"quality": 576, "trusted": true}
|
||||
priority.list[]={"quality": 480, "trusted": true}
|
||||
priority.list[]={"trusted": true}
|
||||
priority.list[]={"quality": 1080}
|
||||
priority.list[]={"quality": 720}
|
||||
priority.list[]={"quality": 576}
|
||||
priority.list[]={"quality": 480}
|
||||
#Empty required to catch what is missing
|
||||
priority.list[]={}
|
8
config/specials.ini
Normal file
8
config/specials.ini
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
#Specials configuration
|
||||
|
||||
handler.specials=true
|
||||
handler.allowed.specials[]=OP
|
||||
handler.allowed.specials[]=ED
|
||||
handler.allowed.specials[]=OVA
|
||||
handler.allowed.specials[]=PV
|
35
config/webfeeds.ini
Normal file
35
config/webfeeds.ini
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include config/feeds/GoodJobMedia.ini
|
||||
#include config/feeds/Commie.ini
|
||||
#include config/feeds/Asenshi.ini
|
||||
#include config/feeds/DameDesuYo.ini
|
||||
#include config/feeds/FFF.ini
|
||||
#include config/feeds/Mezashite.ini
|
||||
#include config/feeds/Chihiro.ini
|
||||
#include config/feeds/Harunatsu.ini
|
||||
#include config/feeds/Hatsuyuki.ini
|
||||
##include config/feeds/SallySubs.ini
|
||||
#include config/feeds/Tsundere.ini
|
||||
#include config/feeds/Coalgirls.ini
|
||||
##include config/feeds/Hiryuu.ini
|
||||
#include config/feeds/SquareSubs.ini
|
||||
#include config/feeds/HoshiNoYume.ini
|
||||
#include config/feeds/UTW.ini
|
||||
#include config/feeds/Kametsu.ini
|
||||
##include config/feeds/Kaitou.ini
|
||||
#include config/feeds/Kantai.ini
|
||||
#include config/feeds/Mori.ini
|
||||
#include config/feeds/gg.ini
|
||||
|
||||
#include config/feeds/GANGO.ini
|
||||
#include config/feeds/PAS.ini
|
||||
|
||||
#Not needed due to having anidex.info already
|
||||
#tracker.webfeed[Doki]=https://anidex.info/rss/group/1
|
||||
tracker.webfeed.Doki.modifyTitle=false
|
||||
tracker.webfeed.Doki.pagination=false
|
||||
tracker.webfeed.Doki.unknown.group=Doki
|
||||
tracker.webfeed.Doki.trusted[Doki]=true
|
||||
tracker.webfeed.Doki.unknown.source.default=TV
|
||||
tracker.webfeed.Doki.unknown.quality.default=720
|
||||
tracker.webfeed.Doki.unknown.videoCodec.default=H264
|
93
data/anidb.extra.dat
Executable file
93
data/anidb.extra.dat
Executable file
|
@ -0,0 +1,93 @@
|
|||
11815|2|x-jat|Koukaku no Pandora
|
||||
11265|2|en|Ajin: Demi-Human
|
||||
11265|2|en|Ajin - Demi-Human
|
||||
11577|2|en|Ajin Season 2
|
||||
11746|1|x-jat|Hibike! Euphonium S2
|
||||
7486|1|x-jat|Shinryaku! Ika Musume S1
|
||||
8294|1|x-jat|Shinryaku! Ika Musume S2
|
||||
10206|2|en|Jojo's Bizarre Adventure Stardust Crusaders Season 01
|
||||
10835|2|en|Jojo's Bizarre Adventure Stardust Crusaders Season 02
|
||||
10960|2|x-jat|Shouwa Genroku Rakugo Shinjuu
|
||||
12062|1|x-jat|Shouwa Genroku Rakugo Shinjuu S2
|
||||
11881|2|x-jat|Sakamoto Desu ga
|
||||
12116|2|x-jat|Zaregoto
|
||||
12116|2|en|Zaregoto 01 - The Beheading Cycle
|
||||
12235|1|x-jat|Little Witch Academia TV
|
||||
12235|2|x-jat|Little Witch Academia
|
||||
12112|2|x-jat|Saiki Kusuo no Psi-nan
|
||||
4900|1|x-jat|Moonlight Mile S1
|
||||
5020|1|x-jat|Moonlight Mile S2
|
||||
7525|3|x-jat|Oreimo S1
|
||||
9134|3|x-jat|Oreimo S2
|
||||
7525|2|x-jat|Ore no Imouto ga Konna ni Kawaii Wake ga Nai First Season
|
||||
9134|2|x-jat|Ore no Imouto ga Konna ni Kawaii Wake ga Nai Second Season
|
||||
9134|1|x-jat|Ore no Imouto ga Konna ni Kawaii Wake ga Nai 2
|
||||
9324|2|en|The World God Only Knows: Tenri-hen
|
||||
12407|2|en|Pokemon the Series: Sun & Moon
|
||||
11905|2|x-jat|Youji Senki
|
||||
919|3|x-jat|Dangaioh
|
||||
12411|2|x-jat|Akiba`s Trip
|
||||
11828|1|x-jat|Shokugeki no Souma S2
|
||||
12136|2|x-jat|ACCA - 13-Territory Inspection Department
|
||||
9348|1|x-jat|Aikatsu!
|
||||
12198|1|x-jat|12-Sai. Chicchana Mune no Tokimeki S2
|
||||
9016|2|en|Folktales from Japan
|
||||
11488|1|x-jat|Binan Koukou Chikyuu Bouei Bu Love! S2
|
||||
8242|2|en|Kaiji 2 - Backslide Arc
|
||||
8242|2|en|Kaiji S2 - Against All Rules
|
||||
8242|1|x-jat|Kaiji S2
|
||||
11343|2|x-jat|Concrete Revolutio
|
||||
11901|2|x-jat|Okusama ga Seitokaichou!+!
|
||||
11690|2|x-jat|Active Raid
|
||||
11835|4|en|Tabi machi Late Show
|
||||
10197|1|x-jat|Seitokai Yakuindomo 2
|
||||
11992|1|x-jat|Kono Subarashii Sekai ni Shukufuku wo! 2
|
||||
11992|0|x-jat|Kono Subarashii Sekai ni Shukufuku wo! 2
|
||||
11982|2|x-jat|Mahou Tsukai no Yome ~Hoshi Matsu Hito~
|
||||
11974|2|x-jat|Sword Oratoria
|
||||
10944|1|x-jat|Shingeki no Kyojin S2
|
||||
10944|1|x-jat|Shingeki no Kyojin (Season 2)
|
||||
10944|1|x-jat|Shingeki no Kyojin 2
|
||||
11221|2|x-jat|Saenai Heroine no Sodatekata ♭
|
||||
11221|2|x-jat|Saenai Heroine no Sodatekata S2
|
||||
11221|2|x-jat|Saekano S2
|
||||
#11739|2|x-jat|Boku no Hero Academia S2
|
||||
12238|2|x-jat|Shuumatsu Nani Shitemasuka Isogashii Desuka Sukutte Moratte Ii Desuka
|
||||
12238|2|x-jat|Shuumatsu Nani Shitemasu ka - Isogashii desu ka - Sukutte Moratte Ii desu ka
|
||||
11480|3|x-jat|Netoge
|
||||
13017|1|x-jat|Youkai Apartment no Yuuga na Nichijou
|
||||
12141|1|en|High School Star Musical S2
|
||||
11991|4|en|Haikyuu!! Karasuno High School vs Shiratorizawa Academy
|
||||
13033|4|en|Owarimonogatari Second Season
|
||||
13033|4|en|Owarimonogatari 2nd Season
|
||||
13033|1|en|Owarimonogatari S2
|
||||
13033|4|en|OWARIMONOGATARI Second Season
|
||||
13033|4|en|OWARIMONOGATARI 2nd Season
|
||||
9332|2|x-jat|Mondaiji-tachi ga Isekai kara Kuru Sou Desu yo
|
||||
712|2|x-jat|Ninku
|
||||
13153|2|x-jat|Youkoso Jitsuryoku Shijou Shugi no Kyoushitsu
|
||||
10321|1|x-jat|Senki Zesshou Symphogear GX
|
||||
12665|2|x-jat|Black Clover (TV)
|
||||
12944|1|x-jat|Osomatsu-san S2
|
||||
12944|2|x-jat|Osomatsu-san 2
|
||||
12227|0|x-jat|UQ Holder! Mahou Sensei Negima! 2
|
||||
12227|2|x-jat|UQ Holder!
|
||||
12712|2|x-jat|Kujira no Kora wa Sajou ni Utau - Children of the Whales
|
||||
12942|2|x-jat|The Ancient Magus' Bride
|
||||
13244|1|x-jat|Shokugeki no Soma S3
|
||||
13256|2|x-jat|Shoujo Shuumatsu Ryokou (Girls' Last Tour)
|
||||
13038|1|x-jat|Itsudatte Bokura no Koi wa 10 cm Datta.
|
||||
12954|1|x-jat|Hozuki no Reitetsu S2
|
||||
12897|1|x-jat|Love Live! Sunshine!! S2
|
||||
12897|2|x-jat|Love Live! Sunshine!! 2nd Season
|
||||
12897|2|x-jat|Love Live! Sunshine!! Season 2
|
||||
162|2|en|Star Gunman Bismarck
|
||||
13385|2|en|Ito Junji Collection
|
||||
12500|2|x-jat|Yuki Yuna wa Yusha de Aru - Yusha no Shou
|
||||
4953|2|en|The Gutsy Frog
|
||||
13152|2|x-jat|Shoujo☆Kageki Revue Starlight
|
||||
13899|2|x-jat|Boogiepop wa Warawanai (2019)
|
||||
531|2|x-jat|Madou King Granzort
|
||||
13795|2|x-jat|Kemurikusa
|
||||
14111|2|x-jat|Kaguya-sama wa Kokurasetai
|
||||
13533|2|x-jat|Date A Live S3
|
54
db.sql
Normal file
54
db.sql
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
CREATE TABLE releases
|
||||
(
|
||||
release_id TEXT PRIMARY KEY,
|
||||
aid INTEGER NOT NULL,
|
||||
release_type INTEGER NOT NULL,
|
||||
release_data jsonb,
|
||||
release_date INTEGER
|
||||
);
|
||||
CREATE INDEX releases_aid_key ON releases(aid);
|
||||
|
||||
CREATE TABLE episodes_releases
|
||||
(
|
||||
aid INTEGER NOT NULL,
|
||||
episode INTEGER NOT NULL,
|
||||
release_id TEXT NOT NULL REFERENCES releases(release_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
release_type INTEGER NOT NULL,
|
||||
release_data jsonb NOT NULL
|
||||
|
||||
);
|
||||
CREATE INDEX episodes_releases_aid_key ON episodes_releases(aid);
|
||||
CREATE INDEX episodes_releases_release_id_key ON episodes_releases(release_id);
|
||||
ALTER TABLE ONLY episodes_releases ADD CONSTRAINT episodes_releases_pkey PRIMARY KEY (release_id, aid, episode, release_type);
|
||||
|
||||
CREATE TABLE tracked_releases
|
||||
(
|
||||
aid INTEGER NOT NULL,
|
||||
episode INTEGER NOT NULL,
|
||||
release_id TEXT NOT NULL REFERENCES releases(release_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
release_type INTEGER NOT NULL,
|
||||
release_data jsonb NOT NULL
|
||||
);
|
||||
CREATE INDEX tracked_releases_aid_key ON tracked_releases(aid);
|
||||
CREATE INDEX tracked_releases_release_id_key ON tracked_releases(release_id);
|
||||
ALTER TABLE ONLY tracked_releases ADD CONSTRAINT tracked_releases_pkey PRIMARY KEY (release_id, aid, episode, release_type);
|
||||
|
||||
CREATE TABLE tracked
|
||||
(
|
||||
aid INTEGER PRIMARY KEY,
|
||||
episode_count INTEGER,
|
||||
prefer_groups TEXT,
|
||||
download_folder TEXT NOT NULL,
|
||||
flags INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE alternate_titles
|
||||
(
|
||||
aid INTEGER,
|
||||
title TEXT NOT NULL,
|
||||
type INTEGER,
|
||||
language TEXT NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY alternate_titles ADD CONSTRAINT alternate_titles_pkey PRIMARY KEY (aid, type, language, title);
|
28
nginx.conf
Normal file
28
nginx.conf
Normal file
|
@ -0,0 +1,28 @@
|
|||
#Place this under sites-available or so
|
||||
|
||||
server {
|
||||
listen 8083 default_server;
|
||||
listen [::]:8083 default_server;
|
||||
|
||||
#Change this to your Animarr folder path
|
||||
root /opt/animarr/Animarr/web;
|
||||
|
||||
# Add index.php to the list if you are using PHP
|
||||
index index.php index.html index.htm index.nginx-debian.html;
|
||||
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
# First attempt to serve request as file, then
|
||||
# as directory, then fall back to displaying a 404.
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# pass PHP scripts to FastCGI server
|
||||
#
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
|
||||
}
|
||||
|
||||
}
|
10
rc.local
Normal file
10
rc.local
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
|
||||
#run this as user animarr
|
||||
su - animarr -c 'cd "/opt/animarr/Animarr" && screen -fa -dmS "animarr" bash -c "./Animarr.sh; exec bash"'
|
||||
|
||||
exit 0
|
401
src/Animarr.php
Normal file
401
src/Animarr.php
Normal file
|
@ -0,0 +1,401 @@
|
|||
<?php
|
||||
|
||||
include "AutoLoader.php";
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Filter;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Source\SourceList;
|
||||
|
||||
ini_set('memory_limit', '2048M');
|
||||
setlocale(LC_CTYPE, "en_US.UTF-8");
|
||||
|
||||
chdir(__DIR__ . "/" . "..");
|
||||
|
||||
Database::parseConfig(isset($argv[1]) ? $argv[1] : "config.ini");
|
||||
Downloader::log("Starting config...");
|
||||
|
||||
|
||||
$aniDBPath = Database::getConfigKey("database.anidb", "/tmp/anidb.cache.dat");
|
||||
//$aniDBExtraPath = Database::getConfigKey("database.anidb.extra", "/tmp/anidb.extra.dat");
|
||||
|
||||
Downloader::log("Loading AniDB...");
|
||||
@mkdir(dirname($aniDBPath), 0755, true);
|
||||
$aniDB = new \Animarr\AniDB($aniDBPath, false);
|
||||
|
||||
Downloader::log("Loading Database...");
|
||||
$database = new Database($aniDB);
|
||||
|
||||
foreach($database->getAlternateTitles() as $t){
|
||||
$aniDB->addEntryName($t["aid"], $t["language"], $t["title"], $t["type"]);
|
||||
}
|
||||
|
||||
$extractor = new \Animarr\Extractor\SceneExtractor($aniDB);
|
||||
|
||||
$sources = new SourceList();
|
||||
|
||||
if(Database::getConfigKey("tracker.animebytes.enable", false)){
|
||||
$abCategory = constant(\Animarr\Source\AnimeBytes::class ."::" . Database::getConfigKey("tracker.animebytes.category", "CATEGORY_SUBS"));
|
||||
|
||||
Downloader::log("Starting AnimeBytes...");
|
||||
$ab = new \Animarr\Source\AnimeBytes(new \Animarr\Extractor\AnimeBytesExtractor($aniDB), $abCategory);
|
||||
$sources->addSource($ab);
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.nyaa.enable", false)){
|
||||
$nyaaFilter = constant(\Animarr\Source\Nyaa::class ."::" . Database::getConfigKey("tracker.nyaa.filter", "FILTER_REMAKES"));
|
||||
$nyaaCategory = constant(\Animarr\Source\Nyaa::class ."::" . Database::getConfigKey("tracker.nyaa.category", "CATEGORY_ANIME_ENGLISH"));
|
||||
|
||||
Downloader::log("Starting Nyaa...");
|
||||
$nyaa = new \Animarr\Source\Nyaa($extractor, $nyaaCategory, $nyaaFilter);
|
||||
$sources->addSource($nyaa);
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.nyaasi.enable", false)){
|
||||
$nyaaSiFilter = constant(\Animarr\Source\NyaaSi::class ."::" . Database::getConfigKey("tracker.nyaasi.filter", "FILTER_REMAKES"));
|
||||
$nyaaSiCategory = constant(\Animarr\Source\NyaaSi::class ."::" . Database::getConfigKey("tracker.nyaasi.category", "CATEGORY_ANIME_ENGLISH"));
|
||||
|
||||
Downloader::log("Starting NyaaSi...");
|
||||
$nyaaSi = new \Animarr\Source\NyaaSi($extractor, $nyaaSiCategory, $nyaaSiFilter);
|
||||
$sources->addSource($nyaaSi);
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.anidex.enable", true)){
|
||||
$anidexFilterMode = constant(\Animarr\Source\AniDex::class ."::" . Database::getConfigKey("tracker.anidex.filterMode", "FILTER_MODE_INCLUDE"));
|
||||
$anidexSelector = constant(\Animarr\Source\AniDex::class ."::" . Database::getConfigKey("tracker.anidex.selector", "SELECTOR_DEFAULT"));
|
||||
$anidexLanguage = constant(\Animarr\Source\AniDex::class ."::" . Database::getConfigKey("tracker.anidex.language", "LANGUAGE_ENGLISH"));
|
||||
|
||||
Downloader::log("Starting AniDex...");
|
||||
$anidex = new \Animarr\Source\AniDex($extractor, $anidexLanguage, $anidexSelector, $anidexFilterMode);
|
||||
$sources->addSource($anidex);
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.hs.enable", false)){
|
||||
Downloader::log("Starting HorribleSubs...");
|
||||
$hs = new \Animarr\Source\HorribleSubs($extractor);
|
||||
$sources->addSource($hs);
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.hsold.enable", false)){
|
||||
Downloader::log("Starting HorribleSubsOld...");
|
||||
$hs = new \Animarr\Source\HorribleSubsOld($extractor);
|
||||
$sources->addSource($hs);
|
||||
}
|
||||
|
||||
foreach(Database::getConfigKey("tracker.webfeed", []) as $name => $feed){
|
||||
Downloader::log("Starting WebFeed $name...");
|
||||
$feed = new \Animarr\Source\WebFeed($extractor, Database::getConfigKey("tracker.webfeed[$name].", $feed), $name);
|
||||
$sources->addSource($feed);
|
||||
}
|
||||
|
||||
$baseFilters = [
|
||||
[Filter::PROPERTY_SEEDS, Filter::LESS_THAN, (int) Database::getConfigKey("filter.minseeds", 2), [
|
||||
[Filter::PROPERTY_UPLOAD_DATE, Filter::LESS_THAN, time() - Database::getConfigKey("filter.newseed.time", 2400), Filter::ACTION_DROP]
|
||||
]],
|
||||
[Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/[ _]preview/i", Filter::ACTION_DROP],
|
||||
//[Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/[ _]short/i", Filter::ACTION_DROP],
|
||||
//[Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/[ _]movie/i", Filter::ACTION_DROP],
|
||||
//[Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/[ _]special/i", Filter::ACTION_DROP],
|
||||
];
|
||||
|
||||
foreach(Database::getConfigKey("filter.keyword.blacklist", []) as $kw){
|
||||
$baseFilters[] = [Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/$kw/i", Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("filter.dubs", true)){
|
||||
$baseFilters[] = [Filter::PROPERTY_ORIGINAL_TITLE, Filter::MATCH, "/(funi|simul)\\-?(dub|dl)/i", Filter::ACTION_DROP];
|
||||
$baseFilters[] = [Filter::PROPERTY_TAGS, Filter::MATCH, "/^(eng|english|)[ ]?dub(bed|)$/i", Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("filter.invalidDownload", true)){
|
||||
$baseFilters[] = [Filter::PROPERTY_DOWNLOAD_LINK, Filter::LAMBDA, function($val){
|
||||
return !Torrent::isTorrentDownloadPath($val);
|
||||
}, Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
foreach (Database::getConfigKey("filter.groups.blacklist", []) as $group){
|
||||
$baseFilters[] = [Filter::PROPERTY_GROUP, Filter::EQUAL_THAN, $group, Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
$extraQuery = "";
|
||||
|
||||
if(Database::getConfigKey("filter.groups.onlyGreyGoldGroups", false)){
|
||||
$extraQuery .= " && (".implode(" | ", Database::getConfigKey("filter.groups.goldlist", [])).")";
|
||||
//foreach(Database::getConfigKey("filter.groups.goldlist", []) as $g){
|
||||
// $extraQuery .= "[$g] |
|
||||
//}
|
||||
$baseFilters[] = [Filter::PROPERTY_GROUP, Filter::LAMBDA, function($group){
|
||||
foreach(array_merge([$group], explode("-", $group)) as $g){
|
||||
|
||||
if(in_array($g, Database::getConfigKey("filter.groups.goldlist", []), true)){
|
||||
return false;
|
||||
}else if(in_array($g, Database::getConfigKey("filter.groups.greylist", []), true)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
$filter = new Filter(Filter::ACTION_KEEP, $baseFilters);
|
||||
|
||||
if(Database::getConfigKey("filter.invalidDownload", true)){
|
||||
$baseFilters[] = [Filter::PROPERTY_DOWNLOAD_LINK, Filter::LAMBDA, function($val){
|
||||
return !Torrent::isTorrentDownloadPath($val);
|
||||
}, Filter::ACTION_DROP];
|
||||
}
|
||||
|
||||
$existingFilter = new Filter(Filter::ACTION_KEEP, $baseFilters);
|
||||
|
||||
|
||||
$selector = new \Animarr\Selector();
|
||||
|
||||
$downloader = new Downloader($database);
|
||||
|
||||
|
||||
$checkLimit = (int) Database::getConfigKey("tracker.startpoll.limit", 1200);
|
||||
|
||||
$counter = 0;
|
||||
$fastCounter = 0;
|
||||
|
||||
/** @var Release[] $releases */
|
||||
$releases = [];
|
||||
|
||||
/*
|
||||
foreach(scandir("data/database/track") as $f){
|
||||
if(preg_match("/anidb\\-([0-9]+)$/", $f, $matches) > 0){
|
||||
$aid = intval($matches[1]);
|
||||
if(!$database->isTracking($aid)){
|
||||
echo "Want to track ".$aniDB->getAnime($aid)["title"]."\n";
|
||||
if(trim(fgets(STDIN)) === "y"){
|
||||
$database->addTrack($aid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($aniDB->getAllMatches("baka to test ni") as $aid => $lev){
|
||||
$info = $aniDB->getAnime($aid);
|
||||
echo $info["title"] . ": $lev\n";
|
||||
}
|
||||
}*/
|
||||
|
||||
$episodes = [];
|
||||
|
||||
if(Database::getConfigKey("refresh.force", false)){
|
||||
foreach($database->getTracked() as $data){
|
||||
Downloader::log("Forcing refresh of \"".$aniDB->getAnime($data["aid"])["title"]."\"!");
|
||||
$episodes[$data["aid"]] = $database->getAnimeEpisodes($data["aid"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while(true){
|
||||
try{
|
||||
|
||||
if(Database::getConfigKey("interval.poll.anidb") !== false and ($counter % Database::getConfigKey("interval.poll.anidb", 172800)) === 0){
|
||||
Downloader::log("Doing anidb update...");
|
||||
foreach($database->getTracked() as $data){
|
||||
$xml = $aniDB->getAnimeData($data["aid"]);
|
||||
Downloader::log("Fetched data of \"".$aniDB->getAnime($data["aid"])["title"]."\": " . strval($xml->type) . " (".$data["episode_count"]."/".$xml->episodecount.")");
|
||||
}
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("interval.poll.new") !== false and ($counter % Database::getConfigKey("interval.poll.new", 60)) === 0){
|
||||
Downloader::log("Fetching new tracked...");
|
||||
foreach($database->getTracked() as $aid => $data){
|
||||
if(!$database->getTrackFlag($aid, Database::FLAG_LOCKED) and (($counter === 0 and Database::getConfigKey("search.force", false)) or $data["episode_count"] <= 0)){
|
||||
Downloader::log("Searching \"".$aniDB->getAnime($aid)["title"]."\" [anidb-$aid]");
|
||||
|
||||
if(($counter === 0 and Database::getConfigKey("search.forceclear", false)) or $data["episode_count"] < 0){
|
||||
$database->clearTrackedEpisodes($aid);
|
||||
$database->updateTrackEpisodes($aid, 0);
|
||||
$data = $database->getTrackInfo($aid);
|
||||
}else if(($counter === 0 and Database::getConfigKey("search.force", false))){
|
||||
$database->updateTrackEpisodes($aid);
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach($database->getAnimeReleases($aid) as $r){
|
||||
$results[] = $r;
|
||||
}
|
||||
|
||||
foreach($database->getAniDB()->getSearchQueries($aid, Database::getConfigKey("anidb.query.languages", ["en", "x-jat"])) as $q){
|
||||
if(strlen($q) < Database::getConfigKey("anidb.query.minlength", 4)){
|
||||
continue;
|
||||
}
|
||||
Downloader::log("Querying \"$q\"");
|
||||
$results = array_merge($results, $sources->find($q, (int) Database::getConfigKey("tracker.firstpoll.limit", -1), $extraQuery));
|
||||
}
|
||||
|
||||
Downloader::log("Found ".count($results)." result(s) [anidb-$aid]");
|
||||
|
||||
foreach($filter->applyFilter($results) as $release){
|
||||
$releases[$release->getId()] = [$release, true];
|
||||
Downloader::log("Found \"".$release->getOriginalTitle()."\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("interval.poll.tracker") !== false and ($counter % Database::getConfigKey("interval.poll.tracker", 10)) === 0){
|
||||
Downloader::log("Doing normal poll...");
|
||||
foreach($filter->applyFilter($sources->find(null, $checkLimit, $extraQuery)) as $release){
|
||||
if(!isset($releases[$release->getId()])){
|
||||
$releases[$release->getId()] = [$release, false];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count($releases) > 0){
|
||||
Downloader::log("Found ".count($releases)." new release(s), checking!");
|
||||
|
||||
foreach ($releases as $id => $r){
|
||||
/** @var Release $result */
|
||||
$result = $r[0];
|
||||
$result->setFilter($filter);
|
||||
Downloader::debug("Checking \"".$result->getOriginalTitle()."\"...");
|
||||
$m = $database->matchRelease($result, Database::getConfigKey("database.forceclean", false));
|
||||
if($m === null){
|
||||
Downloader::log("Could not match \"".$result->getOriginalTitle()."\" (".$result->getTitle().")");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$r[1] and !$m["new"] and !Database::getConfigKey("search.forcenew", false)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(($result->getType() === Release::TYPE_SINGLE and $result->getNumber() >= 0) or $result instanceof MultiRelease){
|
||||
foreach($result->getContents($extractor) as $rr){
|
||||
|
||||
$nm = $database->getAniDB()->matchRelease($rr);
|
||||
|
||||
if($nm === null){
|
||||
Downloader::log("Invalid release \"".$rr->getOriginalTitle()."\" (base: \"".$result->getOriginalTitle()."\")");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($episodes[$nm["aid"]])){
|
||||
$episodes[$nm["aid"]] = [];
|
||||
}
|
||||
|
||||
//if($m["new"]){
|
||||
Downloader::log("Found \"".$rr->getOriginalTitle()."\" for \"".$nm["title"]."\" (base: \"".$result->getOriginalTitle()."\")");
|
||||
//}
|
||||
|
||||
$episodes[$nm["aid"]][$rr->getNumber()] = isset($episodes[$nm["aid"]][$rr->getNumber()]) ? $episodes[$nm["aid"]][$rr->getNumber()] or $m["new"] : $m["new"];
|
||||
}
|
||||
}else{
|
||||
Downloader::log("Invalid release \"".$result->getOriginalTitle()."\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(count($episodes) > 0){
|
||||
Downloader::log("Found ".count($episodes)." new shows(s) to match!");
|
||||
|
||||
foreach ($episodes as $aid => $list){
|
||||
Downloader::debug("Processing anidb-".$aid."...");
|
||||
if(!$database->isTracking($aid) or $database->getTrackFlag($aid, Database::FLAG_LOCKED)){
|
||||
continue;
|
||||
}
|
||||
$eList = $database->getAnimeEpisodes($aid);
|
||||
|
||||
$info = $database->getAniDB()->getAnime($aid);
|
||||
$groups = $database->getTrackGroups($aid);
|
||||
|
||||
foreach ($list as $episode => $check){
|
||||
$trackEpisode = $database->getTrackEpisode($aid, $episode);
|
||||
if($trackEpisode !== null){
|
||||
$trackEpisode = $existingFilter->applyFilterSingle($trackEpisode);
|
||||
}
|
||||
|
||||
if(isset($eList[$episode])){
|
||||
$best = $selector->findBestRelease($filter->applyFilter($eList[$episode]), $groups);
|
||||
if($best === null){
|
||||
continue;
|
||||
}
|
||||
if($trackEpisode === null){
|
||||
$best->setFilter($filter);
|
||||
$database->saveTrackEpisode($aid, $episode, $best);
|
||||
$downloader->addToDownload($aid, $best);
|
||||
}else{
|
||||
if($trackEpisode->getId() === $best->getId()){ //They are the same!
|
||||
continue;
|
||||
}
|
||||
|
||||
if($selector->selectBest($trackEpisode, $best, $groups) === $trackEpisode){ //Other is still best!
|
||||
continue;
|
||||
}
|
||||
$best->setFilter($filter);
|
||||
$database->saveTrackEpisode($aid, $episode, $best);
|
||||
|
||||
$downloader->addToDownload($aid, $best);
|
||||
}
|
||||
|
||||
$tList = $database->getTrackEpisodes($aid);
|
||||
Downloader::log("Matching new episode $episode for \"".$info["title"]."\": \"".$best->getOriginalTitle()."\"");
|
||||
}
|
||||
}
|
||||
|
||||
$database->updateTrackEpisodes($aid);
|
||||
}
|
||||
|
||||
Downloader::log("Trying to download torrents...");
|
||||
$downloader->handleNewDownloads();
|
||||
}
|
||||
|
||||
|
||||
if(Database::getConfigKey("interval.poll.finished") !== false and ($counter % Database::getConfigKey("interval.poll.finished", 5)) === 0){
|
||||
Downloader::log("Polling finished downloads...");
|
||||
$downloader->setUseLink(false);
|
||||
$added = $downloader->handleCompletedDownloads(Database::getConfigKey("torrent.folder.finished", "./Processing"));
|
||||
$downloader->setUseLink(true);
|
||||
$added += $downloader->handleCompletedDownloads(Database::getConfigKey("torrent.folder.private.finished", "./ProcessingPrivate"));
|
||||
|
||||
if($added > 0){
|
||||
foreach(Database::getConfigKey("webhook.episodes.added", []) as $url){
|
||||
\Animarr\Request::getURL($url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if($save){
|
||||
// Downloader::log("Saving database...", "SAVE");
|
||||
// $database->save();
|
||||
// Downloader::log("Database saved!", "SAVE");
|
||||
//}
|
||||
|
||||
$releases = [];
|
||||
|
||||
$episodes = [];
|
||||
|
||||
sleep(60);
|
||||
$checkLimit = (int) Database::getConfigKey("tracker.poll.limit", 0);
|
||||
|
||||
$counter++;
|
||||
|
||||
if(file_exists("dopoll.flag")){
|
||||
unlink("dopoll.flag");
|
||||
$counter = Database::getConfigKey("interval.poll.new", 60) * Database::getConfigKey("interval.poll.finished", 5) * Database::getConfigKey("interval.poll.tracker", 10);
|
||||
$aniDB->resetCache($aniDBPath, false);
|
||||
foreach($database->getAlternateTitles() as $t){
|
||||
$aniDB->addEntryName($t["aid"], $t["language"], $t["title"], $t["type"]);
|
||||
}
|
||||
}
|
||||
|
||||
Torrent::$urls = [];
|
||||
}catch(Exception $e){
|
||||
Downloader::log($e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//$downloader = new \Animarr\Downloader($aniDB)
|
||||
|
||||
//https://img7.anidb.net/pics/anime/177065.jpg
|
313
src/Animarr/AniDB.php
Normal file
313
src/Animarr/AniDB.php
Normal file
|
@ -0,0 +1,313 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class AniDB{
|
||||
|
||||
const ANIDB_API_ENDPOINT = "http://api.anidb.net:9001/httpapi";
|
||||
const ANIDB_API_CLIENT = "animarr";
|
||||
const ANIDB_API_CLIENTVER = 2;
|
||||
const ANIDB_API_PROTOVER = 1;
|
||||
|
||||
private static $ANIDB_API_LAST_REQUEST = 0;
|
||||
|
||||
public function __construct($path, $force = false){
|
||||
$this->initCache($path, $force);
|
||||
$this->loadExceptions();
|
||||
}
|
||||
|
||||
public function getAnimeAPI($aid, $fetch = true, $force = false){
|
||||
if(!$force and file_exists(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml")){
|
||||
$str = file_get_contents(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml");
|
||||
if(trim($str) != "" and trim($str) != '<error code="500">banned</error>'){
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$fetch){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(self::$ANIDB_API_LAST_REQUEST >= time() - 2){
|
||||
sleep(2);
|
||||
}
|
||||
$xml = Request::getURL(self::ANIDB_API_ENDPOINT . "?request=anime&client=" . self::ANIDB_API_CLIENT . "&clientver=" . self::ANIDB_API_CLIENTVER . "&protover=" . self::ANIDB_API_PROTOVER ."&aid=" . $aid);
|
||||
file_put_contents(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml", $xml);
|
||||
self::$ANIDB_API_LAST_REQUEST = time();
|
||||
return $xml;
|
||||
}
|
||||
|
||||
public function getAnimeData($aid){
|
||||
$xml = new \SimpleXMLElement($this->getAnimeAPI($aid, true, false));
|
||||
$cacheTime = filemtime(Database::getConfigKey("database.api.folder", "./api") . "/$aid.xml");
|
||||
$startTime = \DateTime::createFromFormat('Y-m-d', strval($xml->startdate));
|
||||
$endTime = isset($xml->enddate) ? \DateTime::createFromFormat('Y-m-d', strval($xml->enddate)) : PHP_INT_MAX;
|
||||
$startTime = $startTime instanceof \DateTime ? $startTime->getTimestamp() : 0;
|
||||
$endTime = $endTime instanceof \DateTime ? $endTime->getTimestamp() : PHP_INT_MAX;
|
||||
if(
|
||||
$endTime >= (time() - (Database::getConfigKey("database.api.enddelay", 336) * 3600))
|
||||
and
|
||||
(time() - $cacheTime) >= (Database::getConfigKey("database.api.refreshdelay", 48) * 3600)
|
||||
){
|
||||
$xml = new \SimpleXMLElement($this->getAnimeAPI($aid, true, true));
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
private function loadExceptions(){
|
||||
foreach(Database::getConfigKey("anidb.exceptions", "[]") as $exception){
|
||||
$exception = json_decode($exception);
|
||||
if(!isset($this->exceptions[$exception->aid])){
|
||||
$this->exceptions[$exception->aid] = [];
|
||||
}
|
||||
|
||||
$this->exceptions[$exception->aid][] = $exception;
|
||||
}
|
||||
}
|
||||
|
||||
public function matchRelease(Release $release){
|
||||
$match = $this->matchTitle($release->getTitle());
|
||||
if($match !== null and isset($this->exceptions[$match["aid"]])){
|
||||
foreach($this->exceptions[$match["aid"]] as $exception){
|
||||
if(isset($exception->group) and $exception->group !== $release->getGroup()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isset($exception->title) and $exception->title !== $release->getTitle()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$match = $this->getAnime($exception->newAid);
|
||||
}
|
||||
}
|
||||
|
||||
return $match;
|
||||
}
|
||||
|
||||
public function getAll(){
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
public function getAllMatches($title, $limit = 20){
|
||||
$ids = [];
|
||||
$ids2 = [];
|
||||
$cleanTitle = strtolower($title);
|
||||
|
||||
foreach ($this->titleToId as $t => $aid){
|
||||
$lev = levenshtein($cleanTitle, $t);
|
||||
if($lev < 0 or isset($ids[$aid]) and $ids[$aid] < $lev){
|
||||
continue;
|
||||
}
|
||||
$ids[$aid] = $lev;
|
||||
}
|
||||
|
||||
$cleanTitle = $this->cleanTitle($cleanTitle);
|
||||
|
||||
foreach ($this->titleToId as $t => $aid){
|
||||
if(strpos($t, $cleanTitle) !== false){
|
||||
$lev = levenshtein($cleanTitle, $t);
|
||||
if($lev < 0 or isset($ids2[$aid]) and $ids2[$aid] < $lev){
|
||||
continue;
|
||||
}
|
||||
$ids2[$aid] = $lev;
|
||||
}
|
||||
}
|
||||
|
||||
asort($ids);
|
||||
asort($ids2);
|
||||
|
||||
$returnIds = [];
|
||||
|
||||
while((count($returnIds) < $limit or $limit === -1) and (count($ids) > 0 or count($ids2) > 0)){
|
||||
foreach ($ids2 as $aid => $lev){
|
||||
$returnIds[$aid] = $lev;
|
||||
unset($ids2[$aid]);
|
||||
break;
|
||||
}
|
||||
foreach ($ids as $aid => $lev){
|
||||
$returnIds[$aid] = $lev;
|
||||
unset($ids[$aid]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//asort($returnIds);
|
||||
|
||||
return $returnIds;
|
||||
}
|
||||
|
||||
public function matchTitle($title){
|
||||
if($title === ""){
|
||||
return null;
|
||||
}
|
||||
|
||||
$cleanTitle = $title;
|
||||
if(isset($this->titleToId[$cleanTitle])){
|
||||
return $this->db[$this->titleToId[$cleanTitle]];
|
||||
}
|
||||
|
||||
$cleanTitle = strtolower($cleanTitle);
|
||||
if(isset($this->titleToId[$cleanTitle])){
|
||||
return $this->db[$this->titleToId[$cleanTitle]];
|
||||
}
|
||||
|
||||
$cleanTitle = $this->cleanTitle($cleanTitle);
|
||||
if(isset($this->titleToId[$cleanTitle])){
|
||||
return $this->db[$this->titleToId[$cleanTitle]];
|
||||
}
|
||||
|
||||
$cleanTitle = str_replace(" ", "", $cleanTitle);
|
||||
|
||||
if(isset($this->titleToId[$cleanTitle])){
|
||||
return $this->db[$this->titleToId[$cleanTitle]];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAnime($aid){
|
||||
if(isset($this->db[$aid])){
|
||||
return $this->db[$aid];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSearchQueries($aid, $langCodes = ["en", "x-jat"]){
|
||||
if(!isset($this->db[$aid])){
|
||||
return [];
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
|
||||
foreach($this->db[$aid]["languages"] as $langCode => $titles){
|
||||
if(in_array($langCode, $langCodes, true)){
|
||||
foreach($titles as $t){
|
||||
$baseTitle = strtolower($t["title"]);
|
||||
$queries[$baseTitle] = $baseTitle;
|
||||
|
||||
$t = $this->cleanTitle($baseTitle);
|
||||
if(!isset($queries[$t])){
|
||||
$queries[$t] = $t;
|
||||
}
|
||||
|
||||
$t2 = $this->cleanTitle($baseTitle, " ");
|
||||
if(!isset($queries[$t2])){
|
||||
$queries[$t2] = $t2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $queries;
|
||||
}
|
||||
|
||||
private function addTitleToLookup($title, $aid, $priority = false){
|
||||
if(!isset($this->titleToId[$title]) or $priority){
|
||||
$this->titleToId[$title] = $aid;
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanBaseTitle($title, $replace = ""){
|
||||
return trim(preg_replace('!\s+!', ' ', str_ireplace(["the animation"], $replace, $title)));
|
||||
}
|
||||
|
||||
private function cleanTitle($title, $replace = ""){
|
||||
return trim(preg_replace('!\s+!', ' ', str_replace([" o "], [" wo "], str_replace([",", ":", "~", "-", "'", '"', "`", "/", "&", ".", "!", "the animation", " + "], $replace, strtolower($title)))));
|
||||
}
|
||||
|
||||
private function initCache($path, $force = false){
|
||||
if($force !== null and ($force or !file_exists($path) or filemtime($path) < (time() - 3600 * 36))){ //Download every 36 hours
|
||||
$contents = file_get_contents(Database::getConfigKey("anidb.titles.url", "http://anidb.net/api/anime-titles.dat.gz"));
|
||||
if(strlen($contents) > 4096){
|
||||
file_put_contents($path, zlib_decode($contents));
|
||||
}
|
||||
touch($path);
|
||||
}
|
||||
|
||||
foreach(@file($path) as $line){
|
||||
if($line{0} == "#"){
|
||||
continue;
|
||||
}
|
||||
$fields = explode("|", trim($line));
|
||||
$aid = (int) $fields[0];
|
||||
$entryType = (int) $fields[1];
|
||||
$langCode = $fields[2];
|
||||
$title = $fields[3];
|
||||
|
||||
$this->addEntryName($aid, $langCode, $title, $entryType);
|
||||
}
|
||||
}
|
||||
|
||||
public function resetCache($path, $force = false){
|
||||
$this->db = [];
|
||||
$this->initCache($path, $force);
|
||||
}
|
||||
|
||||
public function loadExtra($path){
|
||||
@$this->initCache($path, null);
|
||||
}
|
||||
|
||||
public function addEntryName($aid, $langCode, $title, $entryType = 2){
|
||||
if(!isset($this->db[$aid])){
|
||||
$this->db[$aid] = [
|
||||
"aid" => $aid,
|
||||
"title" => null,
|
||||
"languages" => []
|
||||
];
|
||||
}
|
||||
|
||||
if(!isset($this->db[$aid]["languages"][$langCode])){
|
||||
$this->db[$aid]["languages"][$langCode] = [];
|
||||
}
|
||||
|
||||
if($title !== $this->cleanBaseTitle($title)){
|
||||
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType, "title" => $title];
|
||||
$title = $this->cleanBaseTitle($title);
|
||||
}
|
||||
|
||||
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType, "title" => $title];
|
||||
if($entryType <= 1){
|
||||
$this->db[$aid]["title"] = $title;
|
||||
}
|
||||
|
||||
$this->addTitleToLookup($title, $aid, true);
|
||||
$this->addTitleToLookup(strtolower($title), $aid, true);
|
||||
if(stripos($title, "ka?") !== false){
|
||||
$this->addTitleToLookup(rtrim(str_replace("ka?", "ka", strtolower($title)), '?!'), $aid, true);
|
||||
}
|
||||
|
||||
$this->addTitleToLookup($this->cleanTitle($title), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title)), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete"), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete series"), $aid);
|
||||
|
||||
if(preg_match("/^(.+)([ \t]+season)[ \t]+([0-9])$/iu", $title, $matches) > 0){
|
||||
$title = $matches[1] . " S". $matches[3];
|
||||
$this->db[$aid]["languages"][$langCode][] = ["type" => $entryType === $entryType, "title" => $title];
|
||||
if($entryType === 1){
|
||||
$this->db[$aid]["title"] = $title;
|
||||
}
|
||||
|
||||
$this->addTitleToLookup($title, $aid, true);
|
||||
|
||||
$this->addTitleToLookup(strtolower($title), $aid, true);
|
||||
|
||||
$this->addTitleToLookup($this->cleanTitle($title), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title)), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete"), $aid);
|
||||
$this->addTitleToLookup(str_replace(" ", "", $this->cleanTitle($title) . " complete series"), $aid);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private $titleToId = [];
|
||||
|
||||
private $db = [];
|
||||
|
||||
private $exceptions = [];
|
||||
}
|
425
src/Animarr/Database.php
Normal file
425
src/Animarr/Database.php
Normal file
|
@ -0,0 +1,425 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Release\Release;
|
||||
|
||||
class Database{
|
||||
|
||||
const FLAG_LOCKED = 0b1 << 0;
|
||||
|
||||
private $anidb;
|
||||
private $db;
|
||||
private $extractor;
|
||||
|
||||
private $cacheExists = [];
|
||||
|
||||
private static $config = [];
|
||||
|
||||
public static function parseConfig($path){
|
||||
$lines = file($path);
|
||||
while(count($lines) and ($line = array_shift($lines)) !== null){
|
||||
$line = trim($line);
|
||||
if($line === "" or $line{0} === "#"){
|
||||
if(preg_match("/#include (.*\\.ini)/", $line, $matches) > 0){
|
||||
$lines = array_merge($lines, file($matches[1]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$d = explode("=", $line);
|
||||
$key = trim(array_shift($d));
|
||||
$value = trim(implode("=", $d));
|
||||
|
||||
switch ($value){
|
||||
case "true":
|
||||
case "on":
|
||||
case "yes":
|
||||
$value = true;
|
||||
break;
|
||||
case "false":
|
||||
case "off":
|
||||
case "no":
|
||||
$value = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(preg_match("/^([^\\[\\]]+)\\[([^\\]=]*)\\]$/", $key, $matches) > 0){
|
||||
if(!isset(self::$config[$matches[1]])){
|
||||
self::$config[$matches[1]] = [];
|
||||
}
|
||||
if($matches[2] !== ""){
|
||||
self::$config[$matches[1]][$matches[2]] = $value;
|
||||
}else{
|
||||
self::$config[$matches[1]][] = $value;
|
||||
}
|
||||
}else{
|
||||
self::$config[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getConfigKey($key, $default = null){
|
||||
return isset(self::$config[$key]) ? self::$config[$key] : $default;
|
||||
}
|
||||
|
||||
public function __construct(AniDB $anidb){
|
||||
$this->anidb = $anidb;
|
||||
|
||||
$this->extractor = new SceneExtractor($anidb);
|
||||
|
||||
$user = Database::getConfigKey("database.psql.user");
|
||||
$pass = Database::getConfigKey("database.psql.password");
|
||||
$host = Database::getConfigKey("database.psql.host");
|
||||
$dbname = Database::getConfigKey("database.psql.dbname", "animarr");
|
||||
$this->db = pg_connect("host=$host user=$user password=$pass dbname=$dbname");
|
||||
|
||||
|
||||
//$this->isTracked = @json_decode(file_get_contents($this->path . "/track/all.json"), true);
|
||||
//if(!is_array($this->isTracked)){
|
||||
// $this->isTracked = [];
|
||||
//}
|
||||
}
|
||||
|
||||
public function getAniDB(){
|
||||
return $this->anidb;
|
||||
}
|
||||
|
||||
public function getTracked(){
|
||||
$entries = [];
|
||||
$q = pg_query($this->db, "SELECT * FROM tracked;");
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
$entries[$row["aid"]] = $row;
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
public function getAlternateTitles($aid = null){
|
||||
$entries = [];
|
||||
if($aid === null){
|
||||
$q = pg_query($this->db, "SELECT * FROM alternate_titles;");
|
||||
}else{
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM alternate_titles WHERE aid = $1;', [(int) $aid]);
|
||||
}
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
$entries[] = $row;
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
public function addAlternateTitle($aid, $langCode, $title, $entryType = 2){
|
||||
pg_query_params($this->db, 'INSERT INTO alternate_titles (aid, language, title, type) VALUES ($1, $2, $3, $4);', [$aid, $langCode, $title, $entryType]);
|
||||
$this->anidb->addEntryName($aid, $langCode, $title, $entryType);
|
||||
}
|
||||
|
||||
public function matchRelease(Release $release, $force = false){
|
||||
$match = $this->anidb->matchRelease($release);
|
||||
if($match !== null){
|
||||
return ["aid" => $match["aid"], "new" => $this->addReleaseToDatabase($match["aid"], $release, $force)];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isReleaseInDatabase(Release $release){
|
||||
if(isset($this->cacheExists[$release->getId()])){
|
||||
return $this->cacheExists[$release->getId()];
|
||||
}
|
||||
return $this->cacheExists[$release->getId()] = pg_fetch_assoc(pg_query_params($this->db, 'SELECT COUNT(release_id) as count FROM releases WHERE release_id = $1;', [$release->getId()]))["count"] > 0;
|
||||
}
|
||||
|
||||
private function addReleaseToDatabase($aid, Release $release, $force = false){
|
||||
if($release->getParent() !== null){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($release->getId() === null or $release->getId() === ""){
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
$isInDb = $this->isReleaseInDatabase($release);
|
||||
|
||||
if($isInDb){
|
||||
if(!$force){
|
||||
return false;
|
||||
}
|
||||
|
||||
pg_query_params($this->db, 'DELETE FROM episodes_releases WHERE release_id = $1;', [$release->getId()]);
|
||||
pg_query_params($this->db, 'INSERT INTO releases (release_id, aid, release_type, release_data, release_date) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT releases_pkey DO UPDATE SET aid = $2, release_type = $3, release_data = $4, release_date = $5;', [$release->getId(), $aid, $release->getType(), $release->encode(), $release->getUploadDate()]);
|
||||
}else{
|
||||
pg_query_params($this->db, 'INSERT INTO releases (release_id, aid, release_type, release_data, release_date) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT releases_pkey DO UPDATE SET aid = $2, release_type = $3, release_data = $4, release_date = $5;', [$release->getId(), $aid, $release->getType(), $release->encode(), $release->getUploadDate()]);
|
||||
}
|
||||
|
||||
unset($this->cacheExists[$release->getId()]);
|
||||
|
||||
if($release instanceof MultiRelease){
|
||||
foreach ($release->getContents($this->extractor) as $r){
|
||||
$m = $this->anidb->matchRelease($r);
|
||||
if($r->getType() === Release::TYPE_SINGLE and $r->getNumber() >= 0 and $m !== null/* and $m["aid"] === $aid*/){
|
||||
pg_query_params($this->db, 'INSERT INTO episodes_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT episodes_releases_pkey DO UPDATE SET aid = $1, episode = $2, release_id = $3, release_type = $4, release_data = $5;', [$m["aid"], $r->getNumber(), $r->getId(), $r->getType(), $r->encode()]);
|
||||
}
|
||||
}
|
||||
}elseif($release->getType() === Release::TYPE_SINGLE and $release->getNumber() >= 0){
|
||||
pg_query_params($this->db, 'INSERT INTO episodes_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5);', [$aid, $release->getNumber(), $release->getId(), $release->getType(), $release->encode()]);
|
||||
}
|
||||
|
||||
return !$isInDb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @return Release[][]
|
||||
*/
|
||||
public function getAnimeEpisodes($aid){
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE aid = $1 AND release_type = $2;', [$aid, Release::TYPE_SINGLE]);
|
||||
|
||||
$episodes = [];
|
||||
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
if(!isset($episodes[$row["episode"]])){
|
||||
$episodes[$row["episode"]] = [];
|
||||
}
|
||||
|
||||
$episodes[$row["episode"]][$row["release_id"]] = Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
return $episodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getRecentReleases($limit = 100, $aid = null, $type = null){
|
||||
if($aid !== null){
|
||||
if($type !== null){
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1 AND release_type = $2 ORDER BY release_date DESC NULLS LAST LIMIT $3;', [$aid, $type, $limit]);
|
||||
}else{
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1 ORDER BY release_date DESC NULLS LAST LIMIT $2;', [$aid, $limit]);
|
||||
}
|
||||
}else{
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM releases ORDER BY release_date DESC NULLS LAST LIMIT $1;', [$limit]);
|
||||
}
|
||||
|
||||
$releases = [];
|
||||
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getAnimeBaseReleases($aid){
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM releases WHERE aid = $1;', [$aid]);
|
||||
|
||||
$releases = [];
|
||||
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getAnimeReleases($aid){
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE aid = $1;', [$aid]);
|
||||
|
||||
$releases = [];
|
||||
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
$releases[$row["release_id"]] = Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
public function isTracking($aid){
|
||||
return pg_fetch_assoc(pg_query_params($this->db, 'SELECT COUNT(aid) as count FROM tracked WHERE aid = $1;', [$aid]), null)["count"] > 0;
|
||||
}
|
||||
|
||||
public function getTrackInfo($aid){
|
||||
return pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM tracked WHERE aid = $1;', [$aid]), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @param $episode
|
||||
* @return MultiRelease|Release|null
|
||||
*/
|
||||
public function getTrackEpisode($aid, $episode){
|
||||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM tracked_releases WHERE aid = $1 AND episode = $2 AND release_type = $3;', [$aid, $episode, Release::TYPE_SINGLE]), null);
|
||||
|
||||
return $row !== false ? Release::undecode($row["release_data"]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aid
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getTrackEpisodes($aid){
|
||||
$q = pg_query_params($this->db, 'SELECT * FROM tracked_releases WHERE aid = $1 AND release_type = $2 ORDER BY episode ASC;', [$aid, Release::TYPE_SINGLE]);
|
||||
|
||||
$episodes = [];
|
||||
|
||||
while(($row = pg_fetch_assoc($q, null)) !== false){
|
||||
if(isset($episodes[$row["episode"]])){
|
||||
Downloader::log("Duplicate episode ".$row["episode"]." for tracked anidb-$aid! Some results might be truncated.", "WARNING");
|
||||
$episodes[$row["episode"]] = [];
|
||||
}
|
||||
|
||||
$episodes[$row["episode"]] = Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
|
||||
return $episodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $releaseId
|
||||
* @return Release
|
||||
*/
|
||||
public function getRelease($releaseId){
|
||||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM releases WHERE release_id = $1;', [$releaseId]), null);
|
||||
if(!is_array($row)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $releaseId
|
||||
* @param $episode
|
||||
* @return Release
|
||||
*/
|
||||
public function getReleaseEpisode($releaseId, $episode){
|
||||
$row = pg_fetch_assoc(pg_query_params($this->db, 'SELECT * FROM episodes_releases WHERE release_id = $1 AND episode = $2;', [$releaseId, $episode]), null);
|
||||
if(!is_array($row)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return Release::undecode($row["release_data"]);
|
||||
}
|
||||
|
||||
public function clearTrackedEpisodes($aid){
|
||||
pg_query_params($this->db, 'DELETE FROM tracked_releases WHERE aid = $1;', [$aid]);
|
||||
}
|
||||
|
||||
public function clearEpisodes($aid){
|
||||
pg_query_params($this->db, 'DELETE FROM episodes_releases WHERE aid = $1;', [$aid]);
|
||||
pg_query_params($this->db, 'DELETE FROM releases WHERE aid = $1;', [$aid]);
|
||||
}
|
||||
|
||||
public function saveTrackEpisode($aid, $episode, Release $release = null){
|
||||
if($release === null){
|
||||
pg_query_params($this->db, 'DELETE FROM tracked_releases WHERE aid = $1 AND episode = $2;', [$aid, $episode]);
|
||||
}else{
|
||||
if($this->getTrackEpisode($aid, $episode) !== null){ //Update
|
||||
pg_query_params($this->db, 'UPDATE tracked_releases SET aid = $1, episode = $2, release_id = $3, release_type = $4, release_data = $5 WHERE aid = $1 AND episode = $2;', [$aid, $episode, $release->getId(), $release->getType(), $release->encode()]);
|
||||
}else{
|
||||
pg_query_params($this->db, 'INSERT INTO tracked_releases (aid, episode, release_id, release_type, release_data) VALUES ($1, $2, $3, $4, $5);', [$aid, $episode, $release->getId(), $release->getType(), $release->encode()]);
|
||||
}
|
||||
}
|
||||
$this->updateTrackEpisodes($aid);
|
||||
}
|
||||
|
||||
public function updateTrackEpisodes($aid, $number = null){
|
||||
if($number === null){
|
||||
pg_query_params($this->db, "UPDATE tracked SET episode_count = (SELECT COUNT(episode) FROM tracked_releases WHERE aid = $1) WHERE aid = $1;", [$aid]);
|
||||
}else{
|
||||
pg_query_params($this->db, "UPDATE tracked SET episode_count = $2 WHERE aid = $1;", [$aid, $number]);
|
||||
}
|
||||
|
||||
return $this->getTrackInfo($aid)["episode_count"];
|
||||
}
|
||||
|
||||
public static function cleanNameForFolder($name){
|
||||
return str_replace(["../", "/..", "/", "\x00"], ["", "", "⁄", ""], $name);
|
||||
}
|
||||
|
||||
public function addTrack($aid, $download_folder = ""){
|
||||
if($download_folder === ""){
|
||||
$download_folder = Database::getConfigKey("download.folder", './Download') . "/[Animarr] " . $this->cleanNameForFolder($this->getAniDB()->getAnime($aid)["title"]) . " [anidb-$aid]/";
|
||||
}
|
||||
if(!$this->isTracking($aid)){
|
||||
pg_query_params($this->db, "INSERT INTO tracked (aid, episode_count, prefer_groups, download_folder) VALUES ($1, -1, '', $2);", [$aid, $download_folder]);
|
||||
}
|
||||
}
|
||||
|
||||
public function stopTrack($aid){
|
||||
if($this->isTracking($aid)){
|
||||
pg_query_params($this->db, "DELETE FROM tracked_releases WHERE aid = $1;", [$aid]);
|
||||
pg_query_params($this->db, "DELETE FROM tracked WHERE aid = $1;", [$aid]);
|
||||
$this->cacheExists = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function getTrackGroups($aid){
|
||||
if($this->isTracking($aid)){
|
||||
return explode(",", base64_decode($this->getTrackInfo($aid)["prefer_groups"]));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getTrackFolder($aid, $default = ""){
|
||||
if($this->isTracking($aid)){
|
||||
$folder = $this->getTrackInfo($aid)["download_folder"];
|
||||
if($folder != ""){
|
||||
return $folder;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function setTrackFolder($aid, $folder){
|
||||
pg_query_params($this->db, "UPDATE tracked SET download_folder = $1 WHERE aid = $2;", [$folder, $aid]);
|
||||
}
|
||||
|
||||
public function getTrackFlag($aid, $flag){
|
||||
if($this->isTracking($aid)){
|
||||
return ($this->getTrackInfo($aid)["flags"] & $flag) != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setTrackFlag($aid, $flag, $value){
|
||||
if($this->isTracking($aid)){
|
||||
$flags = ($this->getTrackInfo($aid)["flags"] & ~($value));
|
||||
if($value){
|
||||
$flags |= $flag;
|
||||
}
|
||||
|
||||
pg_query_params($this->db, "UPDATE tracked SET flags = $1 WHERE aid = $2;", [$flags, $aid]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function saveTrackGroups($aid, array $groups){
|
||||
if($this->isTracking($aid)){
|
||||
pg_query_params($this->db, "UPDATE tracked SET prefer_groups = $1 WHERE aid = $2;", [base64_encode(implode(",", $groups)), $aid]);
|
||||
}
|
||||
}
|
||||
|
||||
public function matchTitle($title){
|
||||
return $this->anidb->matchTitle($title);
|
||||
}
|
||||
|
||||
}
|
404
src/Animarr/Downloader.php
Executable file
404
src/Animarr/Downloader.php
Executable file
|
@ -0,0 +1,404 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Source\Source;
|
||||
use Animarr\Torrent\Torrent;
|
||||
|
||||
class Downloader{
|
||||
private $db;
|
||||
private $extractor;
|
||||
private $download = [];
|
||||
private $ignore = [];
|
||||
private $useLink = false;
|
||||
|
||||
public function __construct(Database $db){
|
||||
$this->log("Starting up...");
|
||||
$this->extractor = new SceneExtractor($db->getAniDB());
|
||||
$this->db = $db;
|
||||
$this->log("Done!");
|
||||
}
|
||||
|
||||
public static function log($log, $type = "INFO"){
|
||||
if(defined("NO_LOG") and NO_LOG){
|
||||
return;
|
||||
}
|
||||
$log = "[$type] > $log" . PHP_EOL;
|
||||
echo $log;
|
||||
file_put_contents(Database::getConfigKey("log.path"), $log, FILE_APPEND);
|
||||
}
|
||||
|
||||
public static function debug($log){
|
||||
self::log($log, "DEBUG");
|
||||
}
|
||||
|
||||
public function setUseLink($val){
|
||||
$this->useLink = (bool) $val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getDownloads(){
|
||||
return $this->download;
|
||||
}
|
||||
|
||||
public function getAutoAddFolder($url){
|
||||
return Torrent::isPrivateTorrent($url) ? Database::getConfigKey("torrent.folder.private.blackhole", "./AutoAdd") : Database::getConfigKey("torrent.folder.blackhole", "./AutoAdd");
|
||||
}
|
||||
|
||||
public function handleNewDownloads(){
|
||||
foreach($this->getDownloads() as $r){
|
||||
if(strpos($r->getDownloadLink(), "urn:btih:") !== false){
|
||||
//Magnet
|
||||
file_put_contents($this->getAutoAddFolder($r->getDownloadLink()) . "/" . $r->getId() . ".magnet", $r->getDownloadLink() . PHP_EOL);
|
||||
}else{
|
||||
file_put_contents($this->getAutoAddFolder($r->getDownloadLink()) . "/" . $r->getId() . ".torrent", Torrent::getTorrent($r->getDownloadLink(), Database::getConfigKey("torrent.cache.folder", null)));
|
||||
}
|
||||
$this->log("Downloaded \"".$r->getOriginalTitle()."\"", "FETCH");
|
||||
}
|
||||
$this->download = [];
|
||||
}
|
||||
|
||||
private function handleDirCompletedDownloads($f, $downloadFolder, $moveToFolder, Release $release, $baseRelease = null){
|
||||
if($release->getType() === Release::TYPE_SINGLE or $release->getType() === Release::TYPE_SPECIAL){
|
||||
$this->log("Directory \"$f\" has single release type!", "WARNING");
|
||||
//$this->ignore[$f] = true;
|
||||
//return false;
|
||||
}
|
||||
$this->log("Processing directory \"$f\"", "POST");
|
||||
$added = $this->handleCompletedDownloads($downloadFolder . "/" . $f, $moveToFolder, $release);
|
||||
|
||||
if(Database::getConfigKey("post.deletefailed", true)){
|
||||
$files = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($downloadFolder . "/" . $f, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($files as $fileinfo) {
|
||||
if($fileinfo->isDir() and !$this->useLink){
|
||||
$this->log("Deleting remaining folder \"".$fileinfo->getRealPath()."\"", "POST");
|
||||
rmdir($fileinfo->getRealPath());
|
||||
}
|
||||
//$todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
|
||||
//$this->log("Deleting remaining \"".$fileinfo->getRealPath()."\"", "POST");
|
||||
//$todo($fileinfo->getRealPath());
|
||||
}
|
||||
}
|
||||
|
||||
if(!$this->useLink){
|
||||
rmdir($downloadFolder . "/" . $f);
|
||||
}
|
||||
|
||||
return $added;
|
||||
}
|
||||
|
||||
|
||||
public function handleCompletedDownloads($downloadFolder, $moveToFolder = null, $baseRelease = null){
|
||||
$added = 0;
|
||||
foreach(scandir($downloadFolder) as $f){
|
||||
if($f{0} === "." or isset($this->ignore[$f])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$oldAdded = $added;
|
||||
|
||||
if($baseRelease !== null and is_dir($downloadFolder . "/" . $f)){
|
||||
$this->log("Ignoring nested folder \"$f\"", "ERROR");
|
||||
continue;
|
||||
}
|
||||
|
||||
$release = $this->extractor->extractInformation($f);
|
||||
if($release === null){
|
||||
if(is_dir($downloadFolder . "/" . $f)){
|
||||
$this->log("Could not match \"$f\", but processing folder", "POST");
|
||||
$added += $this->handleCompletedDownloads($downloadFolder . "/" . $f, $moveToFolder, false);
|
||||
}else{
|
||||
$this->log("Ignoring \"$f\"", "ERROR");
|
||||
}
|
||||
|
||||
|
||||
if($oldAdded === $added){
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->log("Processing \"$f\"", "POST");
|
||||
$info = $this->db->getAniDB()->matchRelease($release);
|
||||
if($info === null){
|
||||
$this->log("Could not match \"$f\" [".$release->getTitle().", type: ".$release->getType()."]!", "ERROR");
|
||||
if(!is_dir($downloadFolder . "/" . $f)){
|
||||
if(!$this->useLink and Database::getConfigKey("post.deletefailed", true)){
|
||||
$this->log("Deleting release \"". $f . "\"", "POST");
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}
|
||||
$this->ignore[$f] = true;
|
||||
}else{
|
||||
$added += $this->handleDirCompletedDownloads($f, $downloadFolder, $moveToFolder, $release, $baseRelease);
|
||||
}
|
||||
|
||||
if($oldAdded === $added){
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!$this->db->isTracking($info["aid"])){
|
||||
if(Database::getConfigKey("post.autotrack", false)){
|
||||
Downloader::log("Auto tracking \"".$info["title"]."\" [anidb-" . $info["aid"] ."]", "TRACK");
|
||||
$this->db->addTrack($info["aid"]);
|
||||
//$this->db->updateTrackEpisodes($info["aid"], -1);
|
||||
}else{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("Match found \"".$info["title"]."\" [anidb-" . $info["aid"]."]", "POST");
|
||||
|
||||
if(is_dir($downloadFolder . "/" . $f)){
|
||||
$added += $this->handleDirCompletedDownloads($f, $downloadFolder, $moveToFolder, $release, $baseRelease);
|
||||
|
||||
if($oldAdded === $added){
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
}else{
|
||||
//TODO: match files to see that they are correct
|
||||
$this->log("Type: ".$release->getType().", episode ". $release->getNumber(), "POST");
|
||||
|
||||
if($release->getType() === Release::TYPE_SPECIAL or $release->getNumber() <= 0){
|
||||
if(!Database::getConfigKey("post.specials", true)){
|
||||
if(!$this->useLink and Database::getConfigKey("post.deletefailed", true)){
|
||||
$this->log("Deleting release \"". $f . "\"", "POST");
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}
|
||||
$this->ignore[$f] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$downloadTo = $this->db->getTrackFolder($info["aid"]) . "/Specials";
|
||||
@mkdir($downloadTo, 0777, true);
|
||||
|
||||
if(file_exists($downloadTo . "/" . $f)){
|
||||
$this->ignore[$f] = true;
|
||||
continue;
|
||||
}
|
||||
}else{
|
||||
|
||||
$downloadTo = $this->db->getTrackFolder($info["aid"]);
|
||||
@mkdir($downloadTo, 0777, true);
|
||||
|
||||
if(file_exists($downloadTo . "/" . $f)){
|
||||
$this->log("Release already exists, aborting!", "ERROR");
|
||||
if(!$this->useLink){
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}
|
||||
|
||||
$this->ignore[$f] = true;
|
||||
continue;
|
||||
}
|
||||
$expected = $this->db->getTrackEpisode($info["aid"], $release->getNumber());
|
||||
if($expected !== null and Database::getConfigKey("post.matchrelease", true)){
|
||||
$c1 = str_replace(" ", "", self::removeExtension(strtolower($expected->getOriginalTitle())));
|
||||
$c2 = str_replace(" ", "", self::removeExtension(strtolower($release->getOriginalTitle())));
|
||||
if($c1 != $c2){
|
||||
$this->log("Expected \"".$expected->getOriginalTitle()."\", got \"".$release->getOriginalTitle()."\"! Check in progress...", "WARN");
|
||||
|
||||
if($expected->getCRC() !== null){
|
||||
if($expected->getCRC() === $release->getCRC()){
|
||||
$this->log("Title CRC matches, continue!", "POST");
|
||||
}else{
|
||||
$this->log("CRC mismatch! [". $expected->getCRC() ." != ". $release->getCRC() ."], aborting!", "ERROR");
|
||||
if(!$this->useLink and Database::getConfigKey("post.deletefailed", true)){
|
||||
$this->log("Deleting release \"". $f . "\"", "POST");
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}else{
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}else if($expected->getGroup() != $release->getGroup() or ($expected->getQuality() !== Release::QUALITY_UNKNOWN and $expected->getQuality() != $release->getQuality()) or ($expected->getSource() !== Release::SOURCE_UNKNOWN and $expected->getSource() != $release->getSource())){
|
||||
$this->log("Release does not match, aborting!", "ERROR");
|
||||
if(!$this->useLink and Database::getConfigKey("post.deletefailed", true)){
|
||||
$this->log("Deleting release \"". $f . "\"", "POST");
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}else{
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("post.verifycrc", false) and $release->getCRC() !== null){
|
||||
$this->log("Checking CRC [". $release->getCRC() ."]", "POST");
|
||||
$CRC = strtoupper(hash_file('crc32b', $downloadFolder . "/" . $f));
|
||||
if($CRC !== $release->getCRC()){
|
||||
$this->log("CRC mismatch! [". $release->getCRC() ." != $CRC], aborting!", "ERROR");
|
||||
if(!$this->useLink and Database::getConfigKey("post.deletefailed", true)){
|
||||
$this->log("Deleting release \"". $f . "\"", "POST");
|
||||
@unlink($downloadFolder . "/" . $f);
|
||||
}else{
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("post.deleteold", false)){
|
||||
foreach ($this->getReleasesInFolder($downloadTo, $info["aid"], $baseRelease) as $r2){
|
||||
if($r2->getType() === $release->getType() and $r2->getNumber() === $release->getNumber()){
|
||||
$this->log("Deleting old release \"". $r2->getOriginalTitle() . "\"", "POST");
|
||||
@unlink($downloadTo . "/" . $r2->getOriginalTitle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$added++;
|
||||
|
||||
if($this->useLink){
|
||||
$this->log("Ref-linking to \"".$downloadTo . "/" . $f."\"", "POST");
|
||||
//if(!self::createReflink($downloadFolder . "/" . $f, $downloadTo . "/" . $f)){
|
||||
//$this->log("Could not reflink, soft-linking!", "POST");
|
||||
if(!link($downloadFolder . "/" . $f, $downloadTo . "/" . $f)){
|
||||
$this->log("Could not link, copying!", "POST");
|
||||
copy($downloadFolder . "/" . $f, $downloadTo . "/" . $f);
|
||||
}
|
||||
//}
|
||||
@chmod($downloadTo . "/" . $f, 0777);
|
||||
}else{
|
||||
$this->log("Moving to \"".$downloadTo . "/" . $f."\"", "POST");
|
||||
if(!rename($downloadFolder . "/" . $f, $downloadTo . "/" . $f)){
|
||||
$this->log("Copy-moving to \"".$downloadTo . "/" . $f."\"", "POST");
|
||||
copy($downloadFolder . "/" . $f, $downloadTo . "/" . $f);
|
||||
unlink($downloadFolder . "/" . $f);
|
||||
}
|
||||
@chmod($downloadTo . "/" . $f, 0777);
|
||||
}
|
||||
|
||||
$this->ignore[$f] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $added;
|
||||
}
|
||||
|
||||
private static function createReflink($origin, $destination){
|
||||
$ret = null;
|
||||
$out = [];
|
||||
system("cp --reflink=always ". escapeshellarg($origin) ." ". escapeshellarg($destination) ."", $out, $ret);
|
||||
return $ret === 0;
|
||||
}
|
||||
|
||||
private static function removeExtension($name){
|
||||
$pos = strrpos($name, ".");
|
||||
if($pos !== false and $pos > strlen($name) - 8){
|
||||
return pathinfo($name, PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param null $aid
|
||||
* @param Release|null $baseRelease
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getReleasesInFolder($path, $aid = null, $baseRelease = null){
|
||||
$releases = [];
|
||||
foreach(scandir($path) as $f) {
|
||||
if ($f{0} === ".") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$release = $this->extractor->extractInformation($f);
|
||||
if($release === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($aid !== null){
|
||||
$info = $this->db->getAniDB()->matchRelease($release);
|
||||
if($info === null or $info["aid"] != $aid){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$releases[] = $release;
|
||||
}
|
||||
|
||||
return $releases;
|
||||
}
|
||||
|
||||
private function cleanNameForFolder($name){
|
||||
return str_replace(["../", "/..", "/", "\x00"], ["", "", "⁄", ""], $name);
|
||||
}
|
||||
|
||||
public function addToDownload($anidbId, Release $release, $moveToFolder = null){
|
||||
$toDownload = $release->getParent() !== null ? $release->getParent() : $release;
|
||||
|
||||
if($moveToFolder !== null){
|
||||
$moveToFolder = $this->db->getTrackFolder($anidbId, $moveToFolder . "/[Animarr] " . $this->cleanNameForFolder($this->db->getAniDB()->getAnime($anidbId)["title"])) . (($release->getType() === Release::TYPE_SPECIAL or $release->getNumber() <= 0) ?"/Specials" : "");
|
||||
}
|
||||
|
||||
if(($toDownload->getType() === Release::TYPE_SINGLE and $release->getNumber() >= 0) or $toDownload instanceof MultiRelease){
|
||||
if($toDownload instanceof MultiRelease and isset($this->download[$toDownload->getId()])){
|
||||
return true;
|
||||
}
|
||||
|
||||
$number = 0;
|
||||
foreach($toDownload->getContents($this->extractor) as $r){
|
||||
if($r->getType() !== Release::TYPE_SINGLE or $r->getNumber() < 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$info = $this->db->getAniDB()->matchRelease($r);
|
||||
if($info === null or $info["aid"] != $anidbId){
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = $this->db->getTrackEpisode($anidbId, $r->getNumber());
|
||||
|
||||
if($existing !== null and $existing->getId() !== $r->getId()){
|
||||
//TODO
|
||||
Downloader::log("There is an already existing episode! \"".$r->getOriginalTitle()."\"", "ADD");
|
||||
continue;
|
||||
//unset($this->download[$episodes[$r->getNumber()]["id"]]);
|
||||
}
|
||||
|
||||
$this->db->saveTrackEpisode($anidbId, $r->getNumber(), $r);
|
||||
|
||||
if($moveToFolder !== null){
|
||||
if(file_exists($moveToFolder . "/" . $r->getOriginalTitle())){
|
||||
Downloader::log("Download already exists! \"".$r->getOriginalTitle()."\"", "ADD");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Downloader::log("Added from batch: \"".$r->getOriginalTitle()."\"", "ADD");
|
||||
++$number;
|
||||
}
|
||||
|
||||
if($number === 0){
|
||||
Downloader::log("Couldn't add any episode!", "ERROR");
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->forceAddToDownload($toDownload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function forceAddToDownload(Release $release){
|
||||
$this->download[$release->getId()] = $release;
|
||||
}
|
||||
|
||||
}
|
75
src/Animarr/Extractor/AnimeBytesExtractor.php
Normal file
75
src/Animarr/Extractor/AnimeBytesExtractor.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Extractor;
|
||||
|
||||
use Animarr\AniDB;
|
||||
use Animarr\Database;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Release\Release;
|
||||
|
||||
class AnimeBytesExtractor implements Extractor{
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct(AniDB $aniDB = null){
|
||||
$this->db = $aniDB;
|
||||
}
|
||||
|
||||
private function tryMatch($title){
|
||||
if($this->db === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->db->matchTitle((string) $title) !== null;
|
||||
}
|
||||
|
||||
//
|
||||
public function extractInformation($releaseTitle, $forceType = null){
|
||||
if(!is_array($forceType)){
|
||||
$forceType = [];
|
||||
}
|
||||
|
||||
parse_str($releaseTitle, $info);
|
||||
|
||||
$tags = array_map("trim", explode("|", $info["info"]));
|
||||
|
||||
$releaseTitle = $info["title"];
|
||||
if($info["aid"] != ""){
|
||||
$releaseTitle = $this->db->getAnime($info["aid"])["title"];
|
||||
}
|
||||
|
||||
$group = "";
|
||||
|
||||
$newTags = [];
|
||||
|
||||
$allow = false;
|
||||
|
||||
foreach($tags as $t){
|
||||
if(preg_match("#subs[ ]*\\((.+)\\)#u", $t, $matches) > 0){
|
||||
$group = $matches[1];
|
||||
}
|
||||
|
||||
foreach($forceType as $type){
|
||||
if(stripos($t, $type) !== false){
|
||||
$allow = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(explode(" ", str_replace(["(", ")"], " ", $t)) as $t2){
|
||||
$newTags[] = trim($t2);
|
||||
}
|
||||
}
|
||||
|
||||
if(!$allow or in_array(strtolower(explode(" ", $tags[1])[0]), Database::getConfigKey("filter.extension.ignore", []))){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(isset($info["edition"]) and preg_match("/Episode ([0-9]+)$/", $info["edition"], $matches) > 0){
|
||||
$episode = (int) $matches[1];
|
||||
return new Release("[$group] $releaseTitle ".str_pad($episode, 2, "0", STR_PAD_LEFT) ." [".implode("][", $tags) . "]", $releaseTitle, $group, Release::TYPE_SINGLE, $newTags, $episode);
|
||||
}else{
|
||||
return new MultiRelease("[$group] $releaseTitle [".implode("][", $tags) . "]", $releaseTitle, $group, Release::TYPE_BATCH, $newTags);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
src/Animarr/Extractor/Extractor.php
Executable file
14
src/Animarr/Extractor/Extractor.php
Executable file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Extractor;
|
||||
|
||||
use Animarr\Release\Release;
|
||||
|
||||
interface Extractor{
|
||||
|
||||
/*
|
||||
* @return Release|null
|
||||
*/
|
||||
public function extractInformation($releaseTitle, $forceType = -1);
|
||||
|
||||
}
|
270
src/Animarr/Extractor/SceneExtractor.php
Executable file
270
src/Animarr/Extractor/SceneExtractor.php
Executable file
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Extractor;
|
||||
|
||||
use Animarr\AniDB;
|
||||
use Animarr\Database;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Release\Release;
|
||||
|
||||
class SceneExtractor implements Extractor{
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct(AniDB $aniDB = null){
|
||||
$this->db = $aniDB;
|
||||
}
|
||||
|
||||
private function tryMatch($title){
|
||||
if($this->db === null){
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->db->matchTitle((string) $title) !== null;
|
||||
}
|
||||
|
||||
public function extractInformation($releaseTitle, $deepMatch = false, &$debug = null){
|
||||
$baseExt = pathinfo(trim(strtolower($releaseTitle)), PATHINFO_EXTENSION);
|
||||
if(in_array($baseExt, Database::getConfigKey("filter.extension.ignore", []))){
|
||||
return null;
|
||||
}
|
||||
|
||||
$id_start = "\\[\\(\\{";
|
||||
$id_end = "\\]\\)\\}";
|
||||
$space = " _\\.";
|
||||
$tag = "^$id_end";
|
||||
$tag_separator = "$space,-";
|
||||
$split = "\\-‒–\\/#";
|
||||
|
||||
$releaseTitle = str_ireplace(["capitulo", "especial", "⁄"], ["episode", "special", "/"], $releaseTitle);
|
||||
|
||||
$episodeWordRegex = "[Ee][Pp](|[\\.]|isode)";
|
||||
|
||||
$episode = "0-9";
|
||||
$name = "^$id_start$id_end";//"{$space}{$split}A-Za-z0-9~:;,'\\+!@?&\\.";
|
||||
|
||||
$specials = "(O[Pp]|E[Dd]|OVA|ONA|OAD|SP|PV|CM|Teaser)";
|
||||
|
||||
$specials_find = [" opening ", " ending ", " movie ", /*" special ", */" picture drama ", " trailer "];
|
||||
|
||||
$episode_match = [
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]?{$episodeWordRegex}[{$space}]*(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))(?P<ename>(([{$space}]+[{$split}][{$space}]+|[{$space}]+).*|))$/u",
|
||||
"/^(?P<title>.*)[{$split}][{$space}]+(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))(?P<ename>[{$space}]+[{$split}][{$space}]+.+)$/u",
|
||||
"/^(?P<title>.*)[{$split}][{$space}]+(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))(?P<ename>(([{$space}]+[{$split}][{$space}]+|[{$space}]+).*|))$/u",
|
||||
];
|
||||
|
||||
$volume_match = [
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]+Vol(|[\\.]|ume)[{$space}]*(?P<number>[{$episode}]+)[\\- ]+((Vol(|[\\.]|ume))|)[\\-{$episode} ]+(END|End|)$/u",
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]+[$id_start]Vol(|[\\.]|ume)[{$space}]*(?P<number>[{$episode}]+)[$id_end](|[\\- ][\\-{$episode} ]+)$/u",
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]+Vol(|[\\.]|ume)[{$space}]*(?P<number>[{$episode}]+)$/u",
|
||||
];
|
||||
|
||||
$batch_match = [
|
||||
"/^(?P<title>.*)[{$space}]+(?P<number>[{$episode}]+[{$split}][{$episode}]+)[{$space}]+(.*)$/u",
|
||||
"/^(?P<title>.*)[{$space}]+[{$split}][{$space}]+(.*)$/u",
|
||||
"/^(?P<title>.*)[{$space}]+(?P<number>[{$episode}]+[{$split}][{$episode}]+)(.*)$/u",
|
||||
];
|
||||
|
||||
$special_match = [
|
||||
"/^(?P<title>.*)[{$split}][{$space}]*(?P<type>NC$specials)[{$space}]*[{$split}]?[{$space}]*(?P<number>[{$episode}]*)[A-Za-z]?(?P<version>([v\\. ][0-9]|))$/u",
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]?(?P<type>NC$specials)[{$space}]*[{$split}]?[{$space}]*(?P<number>[{$episode}]*)[A-Za-z]?(?P<version>([v\\. ][0-9]|))$/u",
|
||||
"/^(?P<title>.*)[{$split}][{$space}]*(?P<type>$specials)[{$space}]*[{$split}]?[{$space}]*(?P<number>[{$episode}]*)[A-Za-z]?(?P<version>([v\\. ][0-9]|))$/u",
|
||||
"/^(?P<title>.*)[{$split}]?[{$space}]?(?P<type>$specials)[{$space}]*[{$split}]?[{$space}]*(?P<number>[{$episode}]*)[A-Za-z]?(?P<version>([v\\. ][0-9]|))$/u",
|
||||
"/^(?P<title>.*)[{$split}][{$space}]*(?P<type>S)(?P<number>[{$episode}]+)[A-Za-z]?(?P<version>([v\\. ][0-9]|))$/u",
|
||||
];
|
||||
|
||||
$name_match = "";
|
||||
$tag_match = "/[{$id_start}](?P<tag>[{$tag}]+)[{$id_end}]/u";
|
||||
|
||||
$base_match = [
|
||||
"/^[{$id_start}](?P<group>[{$tag}]+)[{$id_end}][{$split}]?(?P<name>[{$name}]+(\\(([0-9]{4}|Season [0-9]+|S[0-9]+|TV)\\)[{$name}]*|))(?P<extra>.*)(?P<ext>(\\.[a-z0-9]{2,7}|))$/u",
|
||||
];
|
||||
|
||||
if($deepMatch){
|
||||
$episode_match[] = "/^(?P<title>.*)[{$space}]+(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))(?P<ename>(([{$space}]+[{$split}][{$space}]+|[{$space}]+).*|))$/u";
|
||||
|
||||
//Bad match: [Commie] Silver Spoon 2 [BD 720p AAC]
|
||||
$episode_match[] = "/^(?P<title>.*)[{$space}]+{$episodeWordRegex}[{$space}]*(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))$/u";
|
||||
|
||||
$episode_match[] = "/^(?P<title>.*)([{$space}]+[{$split}]|)[{$space}]+S?(?P<season>[{$episode}]+)([xX]|E)(?P<number>[{$episode}]+)(?P<version>([v\\. ][0-9]|))(?P<ename>(([{$space}]+[{$split}][{$space}]+|[{$space}]+).*|))$/u";
|
||||
|
||||
$batch_match[] = "/^(?P<title>.*)[{$space}]*(?P<version>[v\\. ][0-9])(.*)$/u";
|
||||
$batch_match[] = "/^(?P<title>[{$name}]+(\\([0-9]{4}\\)|))(.*)$/u";
|
||||
|
||||
$base_match[] = "/^[{$id_start}](?P<group>[{$tag}]+)[{$id_end}][{$split}]?(?P<name>[{$name}]+(\\([0-9]{4}|TV\\)|))(?P<extra>.*)(?P<ext>(\\.[a-z0-9]{2,7}|))$/u";
|
||||
|
||||
$base_match[] = "/^(?P<group>)(?P<name>[{$name}]+(\\([0-9]{4}\\)|))(?P<extra>.*)$/u";
|
||||
}
|
||||
|
||||
foreach($base_match as $bmatch){
|
||||
if(preg_match($bmatch, $releaseTitle, $matches) > 0){
|
||||
$group = $matches["group"];
|
||||
$rname = trim(str_replace(str_split("$space"), " ", $matches["name"]));
|
||||
$tags = [];
|
||||
|
||||
$ext = @ltrim(strtolower($matches["ext"]), ".");
|
||||
|
||||
if(in_array($ext, Database::getConfigKey("filter.extension.ignore", []))){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(preg_match_all($tag_match, $matches["extra"], $matches) > 0){
|
||||
foreach($matches["tag"] as $v){
|
||||
$v = str_replace(str_split("$tag_separator"), " ", $v);
|
||||
foreach(explode(" ", $v) as $tag){
|
||||
$tags[] = $tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$vmatch = null;
|
||||
$smatch = null;
|
||||
$ematch = null;
|
||||
$bcmatch = null;
|
||||
|
||||
$isVolume = false;
|
||||
$isSpecial = false;
|
||||
$isBatch = false;
|
||||
|
||||
$number = null;
|
||||
$title = null;
|
||||
$version = null;
|
||||
$specialType = null;
|
||||
|
||||
foreach($volume_match as $r){
|
||||
if(preg_match($r, $rname, $matches) > 0){
|
||||
$isVolume = true;
|
||||
$title = trim($matches["title"], " {$split}");
|
||||
$number = $matches["number"];
|
||||
$version = (isset($matches["version"]) and $matches["version"] !== "") ? $matches["version"] : "v1";
|
||||
$vmatch = $r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$isVolume){
|
||||
foreach($special_match as $r){
|
||||
if(preg_match($r, $rname, $matches) > 0){
|
||||
$title = trim($matches["title"], " -");
|
||||
$number = $matches["number"];
|
||||
$version = (isset($matches["version"]) and $matches["version"] !== "") ? $matches["version"] : "v1";
|
||||
$isSpecial = true;
|
||||
|
||||
$typeConstant = Release::class . "::SPECIAL_TYPE_" . strtoupper($matches["type"]);
|
||||
|
||||
if(defined($typeConstant)){
|
||||
$specialType = $typeConstant;
|
||||
}else{
|
||||
$specialType = Release::SPECIAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
$smatch = $r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$isSpecial and !$deepMatch){
|
||||
foreach($specials_find as $needle){
|
||||
if(stripos($rname . " ", $needle) !== false){
|
||||
$isSpecial = true;
|
||||
continue; //Really strange special, TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$isVolume and !$isSpecial){
|
||||
foreach($episode_match as $r){
|
||||
if(preg_match($r, $rname, $matches) > 0){
|
||||
$title = trim($matches["title"], " -");
|
||||
$number = $matches["number"];
|
||||
$version = (isset($matches["version"]) and $matches["version"] !== "") ? $matches["version"] : "v1";
|
||||
$ematch = $r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(($deepMatch and $number === null) or stripos($title, " batch") !== false or in_array("Batch", $tags) or in_array("batch", $tags) or (!$this->tryMatch($title) and $number === null)){
|
||||
foreach($batch_match as $r){
|
||||
if(preg_match($r, $rname, $matches) > 0){
|
||||
$title = trim($matches["title"], " -");
|
||||
$bcmatch = $r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($title === null){
|
||||
$title = $rname;
|
||||
}
|
||||
|
||||
$isBatch = true;
|
||||
$isVolume = false;
|
||||
$isSpecial = false;
|
||||
}
|
||||
|
||||
if(!$this->tryMatch($title)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($group === ""){
|
||||
$group = "UNKNOWN";
|
||||
}
|
||||
|
||||
if($version != "" and $version === " 5"){
|
||||
//Half episode, abort!
|
||||
return null; //???
|
||||
continue;
|
||||
}
|
||||
|
||||
$r = null;
|
||||
|
||||
if($isVolume){
|
||||
$this->setDebug($debug, $title, $group, $tags, $rname, $version, $number, Release::TYPE_VOLUME, $bmatch, $vmatch, $smatch, $ematch, $bcmatch);
|
||||
$r = new MultiRelease($releaseTitle, $title, $group, Release::TYPE_VOLUME, $tags, $number === null ? -1 : (int) $number);
|
||||
}else if($isBatch){
|
||||
$this->setDebug($debug, $title, $group, $tags, $rname, $version, $number, Release::TYPE_BATCH, $bmatch, $vmatch, $smatch, $ematch, $bcmatch);
|
||||
$r = new MultiRelease($releaseTitle, $title, $group, Release::TYPE_BATCH, $tags);
|
||||
}else if($isSpecial){
|
||||
$this->setDebug($debug, $title, $group, $tags, $rname, $version, $number, Release::TYPE_SPECIAL, $bmatch, $vmatch, $smatch, $ematch, $bcmatch);
|
||||
$r = new Release($releaseTitle, $title, $group, Release::TYPE_SPECIAL, $tags, $number === null ? -1 : (int) $number);
|
||||
if($specialType !== null){
|
||||
$r->setSpecialType($specialType);
|
||||
}
|
||||
}else if($title !== null and $number !== null){
|
||||
$this->setDebug($debug, $title, $group, $tags, $rname, $version, $number, Release::TYPE_SINGLE, $bmatch, $vmatch, $smatch, $ematch, $bcmatch);
|
||||
$r = new Release($releaseTitle, $title, $group, Release::TYPE_SINGLE, $tags, $number === null ? -1 : (int) $number);
|
||||
}
|
||||
|
||||
if($r !== null){
|
||||
if($version !== null and $version{0} === "v"){
|
||||
$r->setVersion((int) trim($version, " v."));
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (!$deepMatch) ? $this->extractInformation($releaseTitle, true, $debug) : null;
|
||||
}
|
||||
|
||||
private function setDebug(&$debug, $title, $group, $tags, $rname, $version, $number, $type, $bmatch, $vmatch, $smatch, $ematch, $bcmatch){
|
||||
//var_dump($title . " " . "$group, $rname, $version, $number, $type");
|
||||
if(is_array($debug)){
|
||||
$debug["title"] = $title;
|
||||
$debug["group"] = $group;
|
||||
$debug["tags"] = implode(" ", $tags);
|
||||
$debug["rname"] = $rname;
|
||||
$debug["number"] = $number;
|
||||
$debug["version"] = $version;
|
||||
$debug["type"] = $type;
|
||||
|
||||
$debug["base_match"] = htmlentities($bmatch);
|
||||
$debug["volume_match"] = htmlentities($vmatch);
|
||||
$debug["special_match"] = htmlentities($smatch);
|
||||
$debug["episode_match"] = htmlentities($ematch);
|
||||
$debug["batch_match"] = htmlentities($bcmatch);
|
||||
}
|
||||
}
|
||||
}
|
232
src/Animarr/Filter.php
Executable file
232
src/Animarr/Filter.php
Executable file
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
use Animarr\Release\Release;
|
||||
|
||||
class Filter{
|
||||
|
||||
const PROPERTY_TITLE = "title";
|
||||
const PROPERTY_ORIGINAL_TITLE = "original_title";
|
||||
const PROPERTY_GROUP = "group";
|
||||
const PROPERTY_NUMBER = "number";
|
||||
const PROPERTY_SEEDS = "seeds";
|
||||
const PROPERTY_TAGS = "tags";
|
||||
const PROPERTY_TYPE = "type";
|
||||
const PROPERTY_QUALITY = "quality";
|
||||
const PROPERTY_TRUSTED = "trusted";
|
||||
const PROPERTY_BEST = "best";
|
||||
const PROPERTY_UPLOAD_DATE = "upload_date";
|
||||
const PROPERTY_DOWNLOAD_LINK = "download_url";
|
||||
|
||||
const GREATHER_THAN = ">";
|
||||
const LESS_THAN = "<";
|
||||
const EQUAL_THAN = "=";
|
||||
const DIFFERENT_THAN = "!=";
|
||||
|
||||
const STARTS_WITH = "sub>";
|
||||
const ENDS_WITH = "<sub";
|
||||
const CONTAINS = ">sub<";
|
||||
const MATCH = ">match<";
|
||||
const LAMBDA = ">lambda<";
|
||||
|
||||
const ACTION_DROP = "drop";
|
||||
const ACTION_KEEP = "keep";
|
||||
const ACTION_FORCE_KEEP = "fkeep";
|
||||
const ACTION_DEFAULT = "default";
|
||||
|
||||
private $defaultAction;
|
||||
private $tree;
|
||||
|
||||
public function __construct($defaultAction, $tree){
|
||||
$this->tree = $tree;
|
||||
$this->defaultAction = $defaultAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Release[] $releases
|
||||
*
|
||||
* @return Release[]
|
||||
*/
|
||||
public function applyFilter(array $releases){
|
||||
$newReleases = [];
|
||||
foreach($releases as $k => $r){
|
||||
$r = $this->applyFilterSingle($r);
|
||||
if($r !== null){
|
||||
$newReleases[$k] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
return $newReleases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Release $r
|
||||
* @return Release
|
||||
*/
|
||||
public function applyFilterSingle(Release $r){
|
||||
$ret = $this->filter($r, self::ACTION_DEFAULT, $this->tree);
|
||||
if($ret === self::ACTION_DEFAULT){
|
||||
$ret = $this->defaultAction;
|
||||
}
|
||||
|
||||
if($ret === self::ACTION_KEEP){
|
||||
return $r;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function filter(Release $release, $current, $tree){
|
||||
foreach($tree as $i){
|
||||
$value = null;
|
||||
$ncurrent = null;
|
||||
|
||||
switch($i[0]){
|
||||
case self::PROPERTY_NUMBER:
|
||||
$value = $release->getNumber();
|
||||
break;
|
||||
case self::PROPERTY_TITLE:
|
||||
$value = $release->getTitle();
|
||||
break;
|
||||
case self::PROPERTY_ORIGINAL_TITLE:
|
||||
$value = $release->getOriginalTitle();
|
||||
break;
|
||||
case self::PROPERTY_TYPE:
|
||||
$value = $release->getType();
|
||||
break;
|
||||
case self::PROPERTY_GROUP:
|
||||
$value = $release->getGroup();
|
||||
break;
|
||||
case self::PROPERTY_QUALITY:
|
||||
$value = $release->getQuality();
|
||||
break;
|
||||
case self::PROPERTY_TRUSTED:
|
||||
$value = $release->isTrusted();
|
||||
break;
|
||||
case self::PROPERTY_SEEDS:
|
||||
$value = $release->getSeeds();
|
||||
break;
|
||||
case self::PROPERTY_BEST:
|
||||
$value = $release->isBest();
|
||||
break;
|
||||
case self::PROPERTY_UPLOAD_DATE:
|
||||
$value = $release->getUploadDate();
|
||||
break;
|
||||
case self::PROPERTY_DOWNLOAD_LINK:
|
||||
$value = $release->getDownloadLink();
|
||||
break;
|
||||
case self::PROPERTY_TAGS:
|
||||
$value = $release->getTags();
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
switch($i[1]){
|
||||
case self::LAMBDA:
|
||||
if(is_callable($i[2]) and call_user_func($i[2], $value) === true){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::GREATHER_THAN:
|
||||
if($value > $i[2]){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::LESS_THAN:
|
||||
if($value < $i[2]){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::EQUAL_THAN:
|
||||
if(is_array($value)){
|
||||
foreach($value as $v){
|
||||
if($v == $i[2]){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
}
|
||||
}else if($value == $i[2]){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::DIFFERENT_THAN:
|
||||
if($value != $i[2]){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
|
||||
case self::MATCH:
|
||||
if(is_array($value)){
|
||||
foreach($value as $v){
|
||||
if(preg_match($i[2], $v) > 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
}
|
||||
}else if(preg_match($i[2], $value) > 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::CONTAINS:
|
||||
if(is_array($value)){
|
||||
foreach($value as $v){
|
||||
if(strpos($value, $v) !== false){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
}
|
||||
}else if(strpos($value, $i[2]) !== false){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::STARTS_WITH:
|
||||
if(is_array($value)){
|
||||
foreach($value as $v){
|
||||
if(strpos($value, $v) === 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
}
|
||||
}else if(strpos($value, $i[2]) === 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
case self::ENDS_WITH:
|
||||
if(is_array($value)){
|
||||
foreach($value as $v){
|
||||
if(strpos(strrev($v), strrev($i[2])) === 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
}
|
||||
}else if(strpos(strrev($value), strrev($i[2])) === 0){
|
||||
$ncurrent = $i[3];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if($ncurrent === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($ncurrent)){
|
||||
$ncurrent = $this->filter($release, $current, $ncurrent);
|
||||
}
|
||||
|
||||
if($ncurrent === self::ACTION_DROP){
|
||||
return self::ACTION_DROP;
|
||||
}
|
||||
|
||||
if($ncurrent === self::ACTION_FORCE_KEEP){
|
||||
return self::ACTION_KEEP;
|
||||
}
|
||||
|
||||
$current = $ncurrent;
|
||||
}
|
||||
|
||||
return $current;
|
||||
}
|
||||
}
|
89
src/Animarr/Release/MultiRelease.php
Executable file
89
src/Animarr/Release/MultiRelease.php
Executable file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Release;
|
||||
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Database;
|
||||
|
||||
class MultiRelease extends Release{
|
||||
|
||||
public function __construct($originalTitle, $title, $group, $type, array $tags, $number = -1){
|
||||
parent::__construct($originalTitle, $title, $group, $type, $tags, $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getContents(SceneExtractor $extractor = null){
|
||||
if($this->content !== null){
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$this->content = [];
|
||||
|
||||
$extractor = $extractor === null ? new SceneExtractor() : $extractor;
|
||||
|
||||
$torrent = Torrent::fromURL($this->getDownloadLink(), Database::getConfigKey("torrent.cache.folder", null));
|
||||
if($torrent === null and strpos($this->getDownloadLink(), "urn:btih:") !== false){
|
||||
//Support magnets?
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
foreach ($torrent->getFiles() as $file){
|
||||
if(count($file["path"]) > 2){ //In subfolder, let's ignore for now
|
||||
continue;
|
||||
}
|
||||
|
||||
$baseName = $file["path"][count($file["path"]) - 1];
|
||||
$release = $extractor->extractInformation($baseName);
|
||||
|
||||
if($release === null or $release->getType() !== Release::TYPE_SINGLE){
|
||||
continue;
|
||||
}
|
||||
|
||||
$release->setInfoLink($this->getInfoLink());
|
||||
$release->setDownloadLink($this->getDownloadLink());
|
||||
$release->setUploadDate($this->getUploadDate());
|
||||
$release->setTrusted($this->isTrusted());
|
||||
$release->setBest($this->isBest());
|
||||
|
||||
$release->setSeeds($this->getSeeds());
|
||||
$release->setSize($this->getSize());
|
||||
|
||||
$release->setParent($this);
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
$release->setSource($this->getSource());
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
$release->setQuality($this->getQuality());
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
$release->setVideoCodec($this->getVideoCodec());
|
||||
}
|
||||
|
||||
if($release->getAudioCodec() === Release::AUDIO_CODEC_UNKNOWN){
|
||||
$release->setAudioCodec($this->getAudioCodec());
|
||||
}
|
||||
|
||||
$release->setProperties($this->getProperties());
|
||||
|
||||
if($this->filter !== null){
|
||||
$release = $this->filter->applyFilterSingle($release);
|
||||
}
|
||||
|
||||
if($release === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->content[] = $release;
|
||||
}
|
||||
|
||||
ksort($this->content);
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
}
|
836
src/Animarr/Release/Release.php
Executable file
836
src/Animarr/Release/Release.php
Executable file
|
@ -0,0 +1,836 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Release;
|
||||
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Filter;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Database;
|
||||
|
||||
class Release{
|
||||
const TYPE_SINGLE = 0;
|
||||
const TYPE_VOLUME = 1;
|
||||
const TYPE_BATCH = 2;
|
||||
const TYPE_SPECIAL = 3;
|
||||
|
||||
const SPECIAL_TYPE_UNKNOWN = -1;
|
||||
const SPECIAL_TYPE_OVA = 0;
|
||||
const SPECIAL_TYPE_MOVIE = 1;
|
||||
const SPECIAL_TYPE_OP = 2;
|
||||
const SPECIAL_TYPE_ED = 3;
|
||||
const SPECIAL_TYPE_NCOP = self::SPECIAL_TYPE_OP;
|
||||
const SPECIAL_TYPE_NCED = self::SPECIAL_TYPE_ED;
|
||||
const SPECIAL_TYPE_PV = 4;
|
||||
const SPECIAL_TYPE_CM = 5;
|
||||
const SPECIAL_TYPE_SP = self::SPECIAL_TYPE_OVA;
|
||||
#const SPECIAL_TYPE_S = self::SPECIAL_TYPE_OVA;
|
||||
|
||||
const QUALITY_UNKNOWN = -1;
|
||||
const QUALITY_480 = 480;
|
||||
const QUALITY_576 = 576;
|
||||
const QUALITY_720 = 720;
|
||||
const QUALITY_1080 = 1080;
|
||||
|
||||
const SOURCE_UNKNOWN = -1;
|
||||
const SOURCE_WEB = 1;
|
||||
const SOURCE_TV = 2;
|
||||
const SOURCE_DVD = 3;
|
||||
const SOURCE_BLURAY = 4;
|
||||
const SOURCE_LASERDISC = 5;
|
||||
|
||||
const VIDEO_CODEC_UNKNOWN = -1;
|
||||
const VIDEO_CODEC_H264_10 = 1;
|
||||
const VIDEO_CODEC_H264 = 2;
|
||||
const VIDEO_CODEC_HEVC = 3;
|
||||
const VIDEO_CODEC_MPEG4 = 4;
|
||||
|
||||
|
||||
const AUDIO_CODEC_UNKNOWN = -1;
|
||||
const AUDIO_CODEC_FLAC = 1;
|
||||
const AUDIO_CODEC_AAC = 2;
|
||||
const AUDIO_CODEC_AC3 = 3;
|
||||
const AUDIO_CODEC_DTS = 3;
|
||||
|
||||
protected $content = null;
|
||||
|
||||
private $originalTitle;
|
||||
private $title;
|
||||
private $type;
|
||||
private $specialType;
|
||||
private $group;
|
||||
private $tags = [];
|
||||
private $number;
|
||||
private $properties = [];
|
||||
|
||||
private $quality;
|
||||
private $source;
|
||||
|
||||
private $audioCodec;
|
||||
private $videoCodec;
|
||||
|
||||
private $infoLink;
|
||||
private $downloadLink;
|
||||
private $uploadDate;
|
||||
private $seeds = -1;
|
||||
private $size;
|
||||
|
||||
private $trusted = false;
|
||||
private $best = false;
|
||||
private $CRC = null;
|
||||
|
||||
private $version = 1;
|
||||
|
||||
/** @var Filter */
|
||||
protected $filter = null;
|
||||
|
||||
|
||||
/**
|
||||
* @return Release[]
|
||||
*/
|
||||
public function getContents(SceneExtractor $extractor = null){
|
||||
if($this->content !== null){
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$this->content = [];
|
||||
|
||||
$extractor = $extractor === null ? new SceneExtractor() : $extractor;
|
||||
|
||||
$torrent = Torrent::fromURL($this->getDownloadLink(), Database::getConfigKey("torrent.cache.folder", null));
|
||||
if($torrent === null and strpos($this->getDownloadLink(), "urn:btih:") !== false){
|
||||
//Support magnets
|
||||
$this->content[$this->getNumber()] = $this;
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
foreach ($torrent->getFiles() as $file){
|
||||
if(count($file["path"]) > 1){ //In subfolder, let's ignore for now
|
||||
continue;
|
||||
}
|
||||
|
||||
$baseName = $file["path"][count($file["path"]) - 1];
|
||||
$release = $extractor->extractInformation($baseName);
|
||||
|
||||
if($release === null or $release->getType() !== Release::TYPE_SINGLE){
|
||||
continue;
|
||||
}
|
||||
|
||||
$release->setInfoLink($this->getInfoLink());
|
||||
$release->setDownloadLink($this->getDownloadLink());
|
||||
$release->setUploadDate($this->getUploadDate());
|
||||
$release->setTrusted($this->isTrusted());
|
||||
$release->setBest($this->isBest());
|
||||
|
||||
$release->setSeeds($this->getSeeds());
|
||||
$release->setSize($this->getSize());
|
||||
|
||||
$release->setParent($this);
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
$release->setSource($this->getSource());
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
$release->setQuality($this->getQuality());
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
$release->setVideoCodec($this->getVideoCodec());
|
||||
}
|
||||
|
||||
if($release->getAudioCodec() === Release::AUDIO_CODEC_UNKNOWN){
|
||||
$release->setAudioCodec($this->getAudioCodec());
|
||||
}
|
||||
|
||||
$release->setProperties($this->getProperties());
|
||||
|
||||
if($this->filter !== null){
|
||||
$release = $this->filter->applyFilterSingle($release);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($release === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->content[$release->getNumber()] = $release;
|
||||
}
|
||||
|
||||
ksort($this->content);
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setProperty($k, $v){
|
||||
$this->properties[$k] = $v;
|
||||
}
|
||||
|
||||
public function getProperty($k, $default = null){
|
||||
return isset($this->properties[$k]) ? $this->properties[$k] : $default;
|
||||
}
|
||||
|
||||
public function setProperties($props){
|
||||
return $this->properties = $props;
|
||||
}
|
||||
|
||||
public function getProperties(){
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
|
||||
public function setFilter(Filter $filter = null){
|
||||
if($filter !== $this->filter){
|
||||
$this->content = null;
|
||||
}
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCRC(){
|
||||
return $this->CRC;
|
||||
}
|
||||
|
||||
/** @var Release */
|
||||
private $parent = null;
|
||||
|
||||
/**
|
||||
* @return Release|null
|
||||
*/
|
||||
public function getParent(){
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Release $parent
|
||||
*/
|
||||
public function setParent(Release $parent){
|
||||
$this->parent = $parent;
|
||||
$this->setFilter($parent->filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBest(): bool{
|
||||
return $this->best;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $best
|
||||
*/
|
||||
public function setBest(bool $best){
|
||||
$this->best = $best;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isTrusted(): bool{
|
||||
return $this->trusted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $trusted
|
||||
*/
|
||||
public function setTrusted(bool $trusted){
|
||||
$this->trusted = $trusted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getVersion(){
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $version
|
||||
*/
|
||||
public function setVersion($version){
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getInfoLink(){
|
||||
return $this->infoLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $infoLink
|
||||
*/
|
||||
public function setInfoLink($infoLink){
|
||||
$this->infoLink = html_entity_decode($infoLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAudioCodec(){
|
||||
return $this->audioCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $audioCodec
|
||||
*/
|
||||
public function setAudioCodec($audioCodec){
|
||||
$this->audioCodec = $audioCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getVideoCodec(){
|
||||
return $this->videoCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $videoCodec
|
||||
*/
|
||||
public function setVideoCodec($videoCodec){
|
||||
$this->videoCodec = $videoCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDownloadLink(){
|
||||
return $this->downloadLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $downloadLink
|
||||
*/
|
||||
public function setDownloadLink($downloadLink){
|
||||
$this->downloadLink = html_entity_decode($downloadLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUploadDate(){
|
||||
return $this->uploadDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $uploadDate
|
||||
*/
|
||||
public function setUploadDate($uploadDate){
|
||||
$this->uploadDate = $uploadDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSeeds(){
|
||||
return $this->seeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $seeds
|
||||
*/
|
||||
public function setSeeds($seeds){
|
||||
$this->seeds = $seeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSize(){
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $size
|
||||
*/
|
||||
public function setSize($size){
|
||||
$this->size = $size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(){
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalTitle(){
|
||||
return $this->originalTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*/
|
||||
public function setTitle($title){
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getType(){
|
||||
return (int) $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
*/
|
||||
public function setType($type){
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getSpecialType(){
|
||||
if($this->specialType === null){
|
||||
$this->guessSpecialType();
|
||||
}
|
||||
return (int) $this->specialType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
*/
|
||||
public function setSpecialType($type){
|
||||
$this->specialType = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getGroup(){
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
public function getSourceString(){
|
||||
switch($this->getSource()){
|
||||
case self::SOURCE_LASERDISC:
|
||||
return "LD";
|
||||
case self::SOURCE_BLURAY:
|
||||
return "BLURAY";
|
||||
case self::SOURCE_DVD:
|
||||
return "DVD";
|
||||
case self::SOURCE_TV:
|
||||
return "TV";
|
||||
case self::SOURCE_WEB:
|
||||
return "WEB";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
public function getTypeString(){
|
||||
switch($this->getType()){
|
||||
case self::TYPE_SINGLE:
|
||||
return "SINGLE";
|
||||
case self::TYPE_VOLUME:
|
||||
return "VOLUME";
|
||||
case self::TYPE_BATCH:
|
||||
return "BATCH";
|
||||
case self::TYPE_SPECIAL:
|
||||
return "SPECIAL";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
public function getQualityString(){
|
||||
return $this->getQuality() > 0 ? $this->getQuality() . "p" : "UNKNOWN";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $group
|
||||
*/
|
||||
public function setGroup($group){
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTags(): array{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $tags
|
||||
*/
|
||||
public function setTags(array $tags){
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNumber(): int{
|
||||
return $this->number;
|
||||
}
|
||||
|
||||
public function getId(){
|
||||
return sha1($this->getDownloadLink());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $number
|
||||
*/
|
||||
public function setNumber(int $number){
|
||||
$this->number = $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getQuality(){
|
||||
return $this->quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quality
|
||||
*/
|
||||
public function setQuality($quality){
|
||||
$this->quality = $quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSource(){
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $source
|
||||
*/
|
||||
public function setSource($source){
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
public function __construct($originalTitle, $title, $group, $type, array $tags, $number = -1){
|
||||
$this->originalTitle = $originalTitle;
|
||||
$this->title = $title;
|
||||
$this->group = $group;
|
||||
$this->type = $type;
|
||||
$this->tags = $tags;
|
||||
$this->number = $number;
|
||||
$this->uploadDate = time();
|
||||
|
||||
$this->guessSource();
|
||||
$this->guessQuality();
|
||||
$this->guessAudioCodec();
|
||||
$this->guessVideoCodec();
|
||||
$this->guessCRC();
|
||||
|
||||
}
|
||||
|
||||
private function guessQuality(){
|
||||
$this->quality = self::QUALITY_UNKNOWN;
|
||||
|
||||
foreach($this->tags as $t){
|
||||
$quality = self::QUALITY_UNKNOWN;
|
||||
|
||||
switch(strtolower($t)){
|
||||
case "1080p":
|
||||
case "1920x1080":
|
||||
case "1440x1080": //4:3
|
||||
case "900p":
|
||||
case "810p":
|
||||
case "bd1080p":
|
||||
$quality = self::QUALITY_1080;
|
||||
break;
|
||||
case "700p":
|
||||
case "720p":
|
||||
case "1280x720":
|
||||
case "1024x768": //4:3, strange res
|
||||
case "1280x960": //4:3, strange res
|
||||
case "768p": //wat https://www.nyaa.se/?page=view&tid=845988
|
||||
case "bd720p":
|
||||
case "hdtv":
|
||||
$quality = self::QUALITY_720;
|
||||
break;
|
||||
case "576p":
|
||||
case "1024x576":
|
||||
case "768x576":
|
||||
case "1024x576p":
|
||||
case "768x576p":
|
||||
$quality = self::QUALITY_576;
|
||||
break;
|
||||
case "640x480":
|
||||
case "848x480":
|
||||
case "852x480":
|
||||
case "720x480":
|
||||
case "720x540":
|
||||
case "640x480p":
|
||||
case "848x480p":
|
||||
case "852x480p":
|
||||
case "720x480p":
|
||||
case "720x540p":
|
||||
case "480p":
|
||||
$quality = self::QUALITY_480;
|
||||
break;
|
||||
}
|
||||
|
||||
if($quality > $this->quality){
|
||||
$this->quality = $quality;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->quality === self::QUALITY_UNKNOWN){
|
||||
$quality = self::QUALITY_UNKNOWN;
|
||||
|
||||
|
||||
if($this->getSource() === self::SOURCE_DVD){
|
||||
$quality = self::QUALITY_576;
|
||||
}else if($this->getSource() === self::SOURCE_BLURAY){
|
||||
$quality = self::QUALITY_720;
|
||||
}if($this->getSource() === self::SOURCE_TV){
|
||||
$quality = self::QUALITY_720;
|
||||
}
|
||||
|
||||
if($quality > $this->quality){
|
||||
$this->quality = $quality;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function guessSpecialType(){
|
||||
$this->specialType = Release::SPECIAL_TYPE_UNKNOWN;;
|
||||
|
||||
foreach(preg_split("/[\\.\s,\\-\\[\\]\\(\\)]+/", $this->originalTitle) as $t){
|
||||
$source = self::SOURCE_UNKNOWN;
|
||||
|
||||
$typeConstant = Release::class . "::SPECIAL_TYPE_" . strtoupper($t);
|
||||
|
||||
if(defined($typeConstant)){
|
||||
$this->specialType = $typeConstant;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function guessSource(){
|
||||
$this->source = self::SOURCE_UNKNOWN;
|
||||
|
||||
foreach($this->tags as $t){
|
||||
$source = self::SOURCE_UNKNOWN;
|
||||
|
||||
switch(strtolower($t)){
|
||||
case "bd":
|
||||
case "bdmux":
|
||||
case "bdremux":
|
||||
case "blu":
|
||||
case "bluray":
|
||||
case "bdrip":
|
||||
case "brrip":
|
||||
case "bd720p":
|
||||
case "bd1080p":
|
||||
$source = self::SOURCE_BLURAY;
|
||||
break;
|
||||
case "hdtv":
|
||||
case "tv":
|
||||
$source = self::SOURCE_TV;
|
||||
break;
|
||||
case "cr":
|
||||
case "web":
|
||||
case "webrip":
|
||||
case "webdl":
|
||||
case "odrip": //On-Demand rip
|
||||
case "dl":
|
||||
case "funidl":
|
||||
$source = self::SOURCE_WEB;
|
||||
break;
|
||||
case "ld":
|
||||
case "ldrip":
|
||||
case "laserdisc":
|
||||
$source = self::SOURCE_LASERDISC;
|
||||
break;
|
||||
case "dvd":
|
||||
case "dvdrip":
|
||||
$source = self::SOURCE_DVD;
|
||||
break;
|
||||
}
|
||||
|
||||
if($source > $this->source){
|
||||
$this->source = $source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function guessAudioCodec(){
|
||||
$this->audioCodec = self::AUDIO_CODEC_UNKNOWN;
|
||||
|
||||
foreach($this->tags as $t){
|
||||
$audioCodec = self::AUDIO_CODEC_UNKNOWN;
|
||||
|
||||
switch(strtolower($t)){
|
||||
case "ac":
|
||||
case "ac3":
|
||||
case "dd":
|
||||
case "truehd":
|
||||
$audioCodec = self::AUDIO_CODEC_AC3;
|
||||
break;
|
||||
case "dts":
|
||||
$audioCodec = self::AUDIO_CODEC_DTS;
|
||||
break;
|
||||
case "flac":
|
||||
case "flac2":
|
||||
case "flac3":
|
||||
case "flac5":
|
||||
case "flacx2":
|
||||
case "flacx3":
|
||||
case "flacx5":
|
||||
$audioCodec = self::AUDIO_CODEC_FLAC;
|
||||
break;
|
||||
case "aac":
|
||||
case "aac2":
|
||||
case "aac5":
|
||||
$audioCodec = self::AUDIO_CODEC_AAC;
|
||||
break;
|
||||
}
|
||||
|
||||
if($audioCodec > $this->audioCodec){
|
||||
$this->audioCodec = $audioCodec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function guessVideoCodec(){
|
||||
$this->videoCodec = self::VIDEO_CODEC_UNKNOWN;
|
||||
|
||||
foreach($this->tags as $t){
|
||||
$videoCodec = self::VIDEO_CODEC_UNKNOWN;
|
||||
|
||||
switch(strtolower($t)){
|
||||
case "265":
|
||||
case "h265":
|
||||
case "x265":
|
||||
case "hevc":
|
||||
case "hevc2":
|
||||
$videoCodec = self::VIDEO_CODEC_HEVC;
|
||||
break;
|
||||
case "264":
|
||||
case "h264":
|
||||
case "x264":
|
||||
case "8bit":
|
||||
$videoCodec = self::VIDEO_CODEC_H264;
|
||||
break;
|
||||
case "10":
|
||||
case "10bit":
|
||||
case "10-bit":
|
||||
case "hi10":
|
||||
case "hi10p":
|
||||
case "hi444p":
|
||||
case "hi444pp":
|
||||
$videoCodec = self::VIDEO_CODEC_H264_10;
|
||||
break;
|
||||
case "divx":
|
||||
case "xvid":
|
||||
case "mpeg4":
|
||||
$videoCodec = self::VIDEO_CODEC_MPEG4;
|
||||
break;
|
||||
}
|
||||
|
||||
if($videoCodec > $this->videoCodec){
|
||||
$this->videoCodec = $videoCodec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function guessCRC(){
|
||||
$this->CRC = null;
|
||||
|
||||
foreach($this->tags as $t){
|
||||
if(strlen($t) === 8 and preg_match("/[A-F0-9]{8}/", strtoupper($t)) > 0){
|
||||
$this->CRC = strtoupper($t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function serialize(){
|
||||
return [
|
||||
"id" => $this->getId(),
|
||||
"name" => $this->getOriginalTitle(),
|
||||
"title" => $this->getTitle(),
|
||||
"number" => $this->getNumber(),
|
||||
"group" => $this->getGroup(),
|
||||
"type" => $this->getType(),
|
||||
"source" => $this->getSource(),
|
||||
"quality" => $this->getQuality(),
|
||||
"version" => $this->getVersion(),
|
||||
"properties" => $this->getProperties(),
|
||||
"tags" => array_values($this->getTags()),
|
||||
"trusted" => $this->isTrusted(),
|
||||
"best" => $this->isBest(),
|
||||
"seeds" => $this->getSeeds(),
|
||||
"infoLink" => $this->getInfoLink(),
|
||||
"specialType" => $this->getSpecialType(),
|
||||
"downloadLink" => $this->getDownloadLink(),
|
||||
"size" => $this->getSize(),
|
||||
"uploadDate" => $this->getUploadDate(),
|
||||
"parent" => $this->getParent() !== null ? $this->getParent()->serialize() : null
|
||||
];
|
||||
}
|
||||
|
||||
public function encode(){
|
||||
return json_encode($this->serialize());
|
||||
}
|
||||
|
||||
public static function undecode($data){
|
||||
return Release::unserialize(json_decode($data, true));
|
||||
}
|
||||
|
||||
public static function unserialize(array $data){
|
||||
/** @var Release $release */
|
||||
$release = null;
|
||||
switch ($data["type"]){
|
||||
case Release::TYPE_SINGLE:
|
||||
$release = new Release($data["name"], $data["title"], $data["group"], $data["type"], $data["tags"], $data["number"]);
|
||||
break;
|
||||
case Release::TYPE_SPECIAL:
|
||||
$release = new Release($data["name"], $data["title"], $data["group"], $data["type"], $data["tags"], $data["number"]);
|
||||
break;
|
||||
case Release::TYPE_VOLUME:
|
||||
$release = new MultiRelease($data["name"], $data["title"], $data["group"], $data["type"], $data["tags"], $data["number"]);
|
||||
break;
|
||||
case Release::TYPE_BATCH:
|
||||
$release = new MultiRelease($data["name"], $data["title"], $data["group"], $data["type"], $data["tags"], $data["number"]);
|
||||
break;
|
||||
}
|
||||
|
||||
if($release !== null){
|
||||
$release->setSource($data["source"]);
|
||||
$release->setQuality($data["quality"]);
|
||||
$release->setTrusted($data["trusted"]);
|
||||
$release->setBest($data["best"]);
|
||||
$release->setSeeds($data["seeds"]);
|
||||
$release->setInfoLink($data["infoLink"]);
|
||||
$release->setDownloadLink($data["downloadLink"]);
|
||||
$release->setSize($data["size"]);
|
||||
$release->setUploadDate($data["uploadDate"]);
|
||||
if(isset($data["properties"])){
|
||||
$release->setProperties($data["properties"]);
|
||||
}
|
||||
|
||||
if(isset($data["specialType"])){
|
||||
$release->setSpecialType($data["specialType"]);
|
||||
}
|
||||
|
||||
if($data["parent"] !== null){
|
||||
$release->setParent(Release::unserialize($data["parent"]));
|
||||
}
|
||||
}
|
||||
|
||||
return $release;
|
||||
}
|
||||
}
|
253
src/Animarr/Request.php
Normal file
253
src/Animarr/Request.php
Normal file
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
class Request{
|
||||
|
||||
private static $USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
|
||||
|
||||
private static $ch = [];
|
||||
private static $cookie = [];
|
||||
|
||||
public static function setCookie($n, $c){
|
||||
|
||||
if($c === null){
|
||||
self::$cookie[$n] = "";
|
||||
}else{
|
||||
self::$cookie[$n] .= $c;
|
||||
}
|
||||
unset(self::$ch[$n]);
|
||||
curl_setopt(self::getHandlerInternal($n), CURLOPT_COOKIE, self::$cookie[$n]);
|
||||
}
|
||||
|
||||
public static function getHandleId($url){
|
||||
return parse_url($url)["host"];
|
||||
}
|
||||
|
||||
public static function disableProxy($n){
|
||||
$ch = self::getHandlerInternal($n);
|
||||
curl_setopt($ch, CURLOPT_PROXY, "");
|
||||
}
|
||||
|
||||
public static function setHTTPProxy($n, $address){
|
||||
$ch = self::getHandlerInternal($n);
|
||||
if(preg_match("/^([^@:]+:[^@:]+)@(.+)$/", $address, $matches) > 0){
|
||||
curl_setopt($ch, CURLOPT_PROXY, $matches[2]);
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $matches[1]);
|
||||
}else{
|
||||
curl_setopt($ch, CURLOPT_PROXY, $address);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
|
||||
}
|
||||
|
||||
public static function setSocks4Proxy($n, $address){
|
||||
$ch = self::getHandlerInternal($n);
|
||||
if(preg_match("/^([^@:]+:[^@:]+)@(.+)$/", $address, $matches) > 0){
|
||||
curl_setopt($ch, CURLOPT_PROXY, $matches[2]);
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $matches[1]);
|
||||
}else{
|
||||
curl_setopt($ch, CURLOPT_PROXY, $address);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
|
||||
}
|
||||
|
||||
public static function setSocks5Proxy($n, $address){
|
||||
$ch = self::getHandlerInternal($n);
|
||||
if(preg_match("/^([^@:]+:[^@:]+)@(.+)$/", $address, $matches) > 0){
|
||||
curl_setopt($ch, CURLOPT_PROXY, $matches[2]);
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $matches[1]);
|
||||
}else{
|
||||
curl_setopt($ch, CURLOPT_PROXY, $address);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
|
||||
}
|
||||
|
||||
private static function getHandlerInternal($n, $page = null, $ignore = false){
|
||||
if($ignore){
|
||||
$ch = curl_init($page);
|
||||
curl_setopt($ch, CURLOPT_COOKIE, self::$cookie[$n]);
|
||||
return $ch;
|
||||
}
|
||||
|
||||
if(!isset(self::$ch[$n]) or self::$ch[$n] === null){
|
||||
self::$ch[$n] = curl_init($page);
|
||||
}else{
|
||||
curl_setopt(self::$ch[$n], CURLOPT_URL, $page);
|
||||
}
|
||||
|
||||
return self::$ch[$n];
|
||||
}
|
||||
|
||||
private static function getHandler($page, $ignore = false){
|
||||
return self::getHandlerInternal(self::getHandleId($page), $page, $ignore);
|
||||
}
|
||||
|
||||
public static function getHeaders($page, array $headers = [], $ignore = false, $timeout = 10){
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: " . self::$USER_AGENT, "Accept-Language: *", "Connection: Keep-Alive", "Keep-Alive: 300"], $headers));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
|
||||
$ret = curl_exec($ch);
|
||||
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200){
|
||||
return "";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function getHeaderURL($page, array $headers = [], $ignore = false, $timeout = 10){
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, false);
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: " . self::$USER_AGENT, "Accept-Language: *"], $headers));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
|
||||
$ret = curl_exec($ch);
|
||||
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200){
|
||||
return "";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function getAuthURL($page, $user, $pwd, array $headers = [], $ignore = false, $timeout = 10){
|
||||
//Downloader::log("$page", "getAuthURL");
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERPWD, "$user:$pwd");
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, false);
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: " . self::$USER_AGENT, "Accept-Language: *", "Accept: */*"], $headers));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
|
||||
$ret = curl_exec($ch);
|
||||
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200){
|
||||
return "";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function getURL($page, array $headers = [], $ignore = false, $timeout = 10){
|
||||
//Downloader::log("$page", "getURL");
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, false);
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: " . self::$USER_AGENT, "Accept-Language: *", "Accept: */*"], $headers));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
|
||||
$ret = curl_exec($ch);
|
||||
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200){
|
||||
return "";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function getFileURL($page, $fp, array $headers = [], $ignore = false, callable $progress = null, $timeout = 30){
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, false);
|
||||
curl_setopt($ch, CURLOPT_POST, 0);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: " . self::$USER_AGENT, "Accept-Language: *"], $headers));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
|
||||
if($progress !== null){
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
|
||||
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $progress);
|
||||
//curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024 * 512);
|
||||
}
|
||||
|
||||
curl_exec($ch);
|
||||
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, null);
|
||||
curl_setopt($ch, CURLOPT_FILE, null);
|
||||
}
|
||||
|
||||
public static function postURL($page, $args, array $headers = [], $ignore = false, $timeout = 10){
|
||||
$ch = self::getHandler($page, $ignore);
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_NOBODY, false);
|
||||
if(isset($args["returnheader"])){
|
||||
unset($args["returnheader"]);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
}else{
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_NOPROGRESS, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "");
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $args);
|
||||
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, "");
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ["User-Agent: " . self::$USER_AGENT, "Accept-Language: *"] + $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
|
||||
return curl_exec($ch);
|
||||
}
|
||||
|
||||
}
|
282
src/Animarr/Selector.php
Executable file
282
src/Animarr/Selector.php
Executable file
|
@ -0,0 +1,282 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr;
|
||||
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Torrent\Torrent;
|
||||
|
||||
class Selector{
|
||||
|
||||
private $priorities = [];
|
||||
|
||||
private $pedanticPriorities = [];
|
||||
|
||||
private $pedanticPreferPublic;
|
||||
|
||||
private $greylist = [];
|
||||
|
||||
private $goldlist = [];
|
||||
|
||||
public function __construct(){
|
||||
foreach (Database::getConfigKey("priority.list", [[]]) as $entry){
|
||||
if($entry{0} === "{"){
|
||||
$entry = json_decode($entry);
|
||||
$p = [];
|
||||
if(isset($entry->source)){
|
||||
$p["source"] = constant(Release::class . "::SOURCE_" . $entry->source);
|
||||
}
|
||||
if(isset($entry->quality)){
|
||||
$p["quality"] = constant(Release::class . "::QUALITY_" . $entry->quality);
|
||||
}
|
||||
if(isset($entry->best)){
|
||||
$p["best"] = $entry->best;
|
||||
}
|
||||
if(isset($entry->trusted)){
|
||||
$p["trusted"] = $entry->trusted;
|
||||
}
|
||||
$this->priorities[] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Database::getConfigKey("priority.pedantic.list", [[]]) as $entry){
|
||||
if($entry{0} === "{"){
|
||||
$entry = json_decode($entry);
|
||||
$p = [];
|
||||
if(isset($entry->videoCodec)){
|
||||
$p["videoCodec"] = constant(Release::class . "::VIDEO_CODEC_" . $entry->videoCodec);
|
||||
}
|
||||
if(isset($entry->audioCodec)){
|
||||
$p["audioCodec"] = constant(Release::class . "::AUDIO_CODEC_" . $entry->audioCodec);
|
||||
}
|
||||
if(isset($entry->crc)){
|
||||
$p["crc"] = $entry->crc;
|
||||
}
|
||||
|
||||
$this->pedanticPriorities[] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
$goldlist = Database::getConfigKey("filter.groups.goldlist", []);
|
||||
|
||||
foreach ($goldlist as $k => $entry){
|
||||
$this->goldlist[strtolower($entry)] = count($goldlist) - $k;
|
||||
}
|
||||
|
||||
$greylist = Database::getConfigKey("filter.groups.greylist", []);
|
||||
|
||||
foreach ($greylist as $entry){
|
||||
$this->greylist[strtolower($entry)] = $entry;
|
||||
}
|
||||
|
||||
$this->pedanticPreferPublic = Database::getConfigKey("priority.pedantic.preferPublic", true);
|
||||
}
|
||||
|
||||
public function calculatePriority(Release $release){
|
||||
$bestMatch = PHP_INT_MAX;
|
||||
|
||||
foreach($this->priorities as $k => $match){
|
||||
if(isset($match["source"]) and $match["source"] !== $release->getSource()){
|
||||
continue;
|
||||
}
|
||||
if(isset($match["quality"]) and $match["quality"] !== $release->getQuality()){
|
||||
continue;
|
||||
}
|
||||
if(isset($match["trusted"]) and $match["trusted"] !== $release->isTrusted()){
|
||||
continue;
|
||||
}
|
||||
if(isset($match["best"]) and $match["best"] !== $release->isBest()){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($bestMatch > $k){
|
||||
$bestMatch = $k;
|
||||
}
|
||||
}
|
||||
|
||||
return $bestMatch;
|
||||
}
|
||||
|
||||
|
||||
public function calculatePedanticPriority(Release $release){
|
||||
$bestMatch = PHP_INT_MAX;
|
||||
|
||||
foreach($this->pedanticPriorities as $k => $match){
|
||||
if(isset($match["videoCodec"]) and $match["videoCodec"] !== $release->getVideoCodec()){
|
||||
continue;
|
||||
}
|
||||
if(isset($match["audioCodec"]) and $match["audioCodec"] !== $release->getAudioCodec()){
|
||||
continue;
|
||||
}
|
||||
if(isset($match["crc"]) and $match["crc"] !== ($release->getCRC() !== null)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($bestMatch > $k){
|
||||
$bestMatch = $k;
|
||||
}
|
||||
}
|
||||
|
||||
return $bestMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Release[] $listing
|
||||
* @param array $preferGroups
|
||||
* @return Release
|
||||
*/
|
||||
public function findBestRelease(array $listing, array $preferGroups = []){
|
||||
$best = null;
|
||||
foreach ($listing as $entry){
|
||||
if($best === null){
|
||||
$best = $entry;
|
||||
continue;
|
||||
}
|
||||
$old = $best;
|
||||
$best = $this->selectBest($best, $entry, $preferGroups);
|
||||
Downloader::log("\"".$best->getOriginalTitle()."\" is better than \"".($old === $best ? $entry->getOriginalTitle() : $old->getOriginalTitle())."\"", "SELECTOR");
|
||||
}
|
||||
|
||||
return $best;
|
||||
}
|
||||
|
||||
private function isInList(Release $release, array $list){
|
||||
$c = isset($list[strtolower($release->getGroup())]) ? 1 : 0;
|
||||
foreach(explode("-", strtolower($release->getGroup())) as $i => $k){
|
||||
if($k != "" and isset($list[$k])){
|
||||
++$c;
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
private function isInPriorityList(Release $release, array $list){
|
||||
$c = isset($list[strtolower($release->getGroup())]) ? $list[strtolower($release->getGroup())] : 0;
|
||||
foreach(explode("-", strtolower($release->getGroup())) as $i => $k){
|
||||
if($k != "" and isset($list[$k])){
|
||||
if($list[$k] > $c){
|
||||
$c = $list[$k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
public function selectBest(Release $e1, Release $e2, array $preferGroups = []){
|
||||
$groups = [];
|
||||
foreach ($preferGroups as $k){
|
||||
$groups[strtolower($k)] = $k;
|
||||
}
|
||||
|
||||
$priority1 = $this->calculatePriority($e1);
|
||||
$priority2 = $this->calculatePriority($e2);
|
||||
|
||||
if(in_array(pathinfo(trim(strtolower($e1->getOriginalTitle())), PATHINFO_EXTENSION), Database::getConfigKey("filter.extension.greylist", []))){
|
||||
$priority1 += 10000;
|
||||
}
|
||||
|
||||
if(in_array(pathinfo(trim(strtolower($e2->getOriginalTitle())), PATHINFO_EXTENSION), Database::getConfigKey("filter.extension.greylist", []))){
|
||||
$priority2 += 10000;
|
||||
}
|
||||
|
||||
if($this->isInList($e1, $this->greylist)){
|
||||
$priority1 += 5000;
|
||||
}
|
||||
|
||||
if($this->isInList($e2, $this->greylist)){
|
||||
$priority2 += 5000;
|
||||
}
|
||||
|
||||
if($this->isInList($e1, $groups)){
|
||||
$priority1 -= 10000;
|
||||
}
|
||||
|
||||
if($this->isInList($e2, $groups)){
|
||||
$priority2 -= 10000;
|
||||
}
|
||||
|
||||
if($e1->getSource() === $e2->getSource()){
|
||||
if($d = $this->isInPriorityList($e1, $this->goldlist)){
|
||||
$priority1 -= 1000;
|
||||
}
|
||||
|
||||
if($d = $this->isInPriorityList($e2, $this->goldlist)){
|
||||
$priority2 -= 1000;
|
||||
}
|
||||
}
|
||||
|
||||
if($priority1 > $priority2){
|
||||
return $e2;
|
||||
}elseif($priority1 < $priority2){
|
||||
return $e1;
|
||||
}
|
||||
|
||||
if($d = $this->isInPriorityList($e1, $this->goldlist)){
|
||||
$priority1 -= 1000 * $d;
|
||||
}
|
||||
|
||||
if($d = $this->isInPriorityList($e2, $this->goldlist)){
|
||||
$priority2 -= 1000 * $d;
|
||||
}
|
||||
|
||||
if($priority1 > $priority2){
|
||||
return $e2;
|
||||
}elseif($priority1 < $priority2){
|
||||
return $e1;
|
||||
}
|
||||
|
||||
//AAAAHAHHA they are the same!
|
||||
//Now starts the fun part!
|
||||
|
||||
$ppriority1 = $this->calculatePedanticPriority($e1);
|
||||
$ppriority2 = $this->calculatePedanticPriority($e2);
|
||||
|
||||
if($ppriority1 > $ppriority2){
|
||||
return $e2;
|
||||
}elseif($ppriority1 < $ppriority2){
|
||||
return $e1;
|
||||
}
|
||||
|
||||
if($this->pedanticPreferPublic){
|
||||
$e1IsPrivate = Torrent::isPrivateTorrent($e1->getDownloadLink());
|
||||
$e2IsPrivate = Torrent::isPrivateTorrent($e2->getDownloadLink());
|
||||
|
||||
if($e1IsPrivate and !$e2IsPrivate){
|
||||
return $e2;
|
||||
}else if(!$e1IsPrivate and $e2IsPrivate){
|
||||
return $e1;
|
||||
}
|
||||
}
|
||||
|
||||
//This handles v2s... mostly?
|
||||
if($e1->getGroup() === $e2->getGroup() and $e1->getNumber() === $e2->getNumber() and $e1->getQuality() === $e2->getQuality() and $e1->getSource() === $e2->getSource() and $e1->getTitle() === $e2->getTitle() and $e1->getType() === $e2->getType()){
|
||||
|
||||
|
||||
if($e1->getVersion() > $e2->getVersion()){
|
||||
return $e1;
|
||||
}else if($e1->getVersion() < $e2->getVersion()){
|
||||
return $e2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($e1->getSeeds() > $e2->getSeeds()){
|
||||
return $e1;
|
||||
}else if($e1->getSeeds() < $e2->getSeeds()){
|
||||
return $e2;
|
||||
}
|
||||
|
||||
|
||||
if($e1->getUploadDate() < $e2->getUploadDate()){
|
||||
return $e1;
|
||||
}else if($e1->getUploadDate() > $e2->getUploadDate()){
|
||||
return $e2;
|
||||
}
|
||||
|
||||
return $e1;
|
||||
}
|
||||
|
||||
|
||||
}
|
187
src/Animarr/Source/AniDex.php
Normal file
187
src/Animarr/Source/AniDex.php
Normal file
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class AniDex implements Source{
|
||||
|
||||
const LANGUAGE_ALL = 0;
|
||||
const LANGUAGE_ENGLISH = 1;
|
||||
const LANGUAGE_SPANISH = "15,29";
|
||||
|
||||
const SELECTOR_BATCH = "&batch=1";
|
||||
const SELECTOR_RAW = "&raw=1";
|
||||
const SELECTOR_REMAKES = "&reencode=1";
|
||||
|
||||
const SELECTOR_DEFAULT = self::SELECTOR_BATCH . self::SELECTOR_REMAKES;
|
||||
|
||||
const FILTER_MODE_INCLUDE = 1;
|
||||
const FILTER_MODE_ONLY = 2;
|
||||
|
||||
private $language;
|
||||
private $selector;
|
||||
private $filterMode;
|
||||
private $uploader = null;
|
||||
private $extractor;
|
||||
|
||||
public function __construct(SceneExtractor $extractor, $language = self::LANGUAGE_ENGLISH, $selector = self::SELECTOR_DEFAULT, $filterMode = self::FILTER_MODE_INCLUDE){
|
||||
$this->language = $language;
|
||||
$this->selector = $selector;
|
||||
$this->filterMode = $filterMode;
|
||||
$this->extractor = $extractor;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "AniDex";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = Database::getConfigKey("tracker.anidex.ajax.baseurl", "https://anidex.info/ajax/page.ajax.php?page=torrents");
|
||||
|
||||
if($query !== null){
|
||||
$url .= "&filename=". urlencode($query);
|
||||
}
|
||||
|
||||
$itemsPerPage = 50;
|
||||
|
||||
$url .= $this->selector;
|
||||
$url .= "&lang_id={$this->language}";
|
||||
$url .= "&filter_mode={$this->filterMode}";
|
||||
$url .= "&limit=$itemsPerPage";
|
||||
$url .= "&category=1";
|
||||
|
||||
if($this->uploader !== null){
|
||||
$url .= "&group_id={$this->uploader}";
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$offset = 0;
|
||||
|
||||
do{
|
||||
$lastCount = count($results);
|
||||
$feed = Request::getURL($url . "&offset=" . ($offset * $itemsPerPage), [
|
||||
"X-Requested-With: XMLHttpRequest",
|
||||
"Referer: https://anidex.info/",
|
||||
]);
|
||||
|
||||
if($feed == ""){
|
||||
break;
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
@$dom->loadHTML($feed);
|
||||
|
||||
foreach($dom->getElementsByTagName("tr") as $item){
|
||||
|
||||
if($item->getElementsByTagName("a")->length <= 0 or preg_match("#torrent#", $item->getElementsByTagName("a")->item(0)->getAttribute("class")) <= 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$isTrusted = ($item->getElementsByTagName("td")->item(1)->getElementsByTagName("span")->item(0) !== null ? true : false);
|
||||
|
||||
$infoLink = trim($item->getElementsByTagName("td")->item(2)->getElementsByTagName("a")->item(0)->getAttribute("href"));
|
||||
$title = trim($item->getElementsByTagName("td")->item(2)->getElementsByTagName("a")->item(0)->getElementsByTagName("span")->item(0)->textContent);
|
||||
$torrentLink = trim($item->getElementsByTagName("td")->item(4)->getElementsByTagName("a")->item(0)->getAttribute("href"));
|
||||
if($infoLink{0} === "?"){
|
||||
$data = parse_url($url);
|
||||
$infoLink = $data["scheme"] . "://" . $data["host"] . "/" . $infoLink;
|
||||
}
|
||||
if($torrentLink{0} === "/"){
|
||||
$data = parse_url($url);
|
||||
$torrentLink = $data["scheme"] . "://" . $data["host"] . $torrentLink;
|
||||
}
|
||||
$magnetLink = trim($item->getElementsByTagName("td")->item(5)->getElementsByTagName("a")->item(0)->getAttribute("href"));
|
||||
$uploadDate = \DateTime::createFromFormat("Y-m-d H:i:s e", (string) $item->getElementsByTagName("td")->item(7)->getAttribute("title"))->getTimestamp();
|
||||
$seeds = intval($item->getElementsByTagName("td")->item(8)->textContent);
|
||||
|
||||
$release = $this->extractor->extractInformation($title);
|
||||
|
||||
if($release !== null){
|
||||
$release->setInfoLink($infoLink);
|
||||
$release->setDownloadLink($torrentLink);
|
||||
$release->setUploadDate($uploadDate);
|
||||
|
||||
$release->setSeeds($seeds);
|
||||
|
||||
if($isTrusted){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
|
||||
if(isset(Database::getConfigKey("tracker.anidex.trusted", [])[$release->getGroup()])){
|
||||
$release->setTrusted(Database::getConfigKey("tracker.anidex.trusted", [])[$release->getGroup()]);
|
||||
}else if(Database::getConfigKey("tracker.anidex.goldlist.trusted", false)){
|
||||
$items = explode("-", strtolower($release->getGroup()));
|
||||
foreach(Database::getConfigKey("filter.groups.goldlist", []) as $gold){
|
||||
if($release->isTrusted()){
|
||||
break;
|
||||
}
|
||||
|
||||
if(strtolower($release->getGroup()) == strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
|
||||
foreach($items as $i){
|
||||
if($i === strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.anidex.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.anidex.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.anidex.unknown.source.default", "TV")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaa.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.anidex.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.anidex.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.anidex.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.anidex.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.anidex.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
++$offset;
|
||||
}while(($maxResults == -1 or count($results) < $maxResults) and $lastCount !== count($results));
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function setUploader($uploader){
|
||||
$this->uploader = ($uploader === null ? null : $uploader);
|
||||
}
|
||||
}
|
||||
|
||||
|
224
src/Animarr/Source/AnimeBytes.php
Normal file
224
src/Animarr/Source/AnimeBytes.php
Normal file
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\AnimeBytesExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class AnimeBytes implements Source{
|
||||
|
||||
const CATEGORY_RAW = "raw";
|
||||
const CATEGORY_SOFTSUBS = "softsubs";
|
||||
const CATEGORY_HARDSUBS = "hardsubs";
|
||||
const CATEGORY_SUBS = [self::CATEGORY_SOFTSUBS, self::CATEGORY_HARDSUBS];
|
||||
const CATEGORY_ALL = [self::CATEGORY_RAW, self::CATEGORY_SOFTSUBS, self::CATEGORY_HARDSUBS];
|
||||
|
||||
|
||||
private $category;
|
||||
private $uploader = null;
|
||||
private $loggedIn;
|
||||
private $extractor;
|
||||
private $username;
|
||||
private $passkey;
|
||||
|
||||
public function __construct(AnimeBytesExtractor $extractor, $category = self::CATEGORY_SUBS){
|
||||
$this->category = !is_array($category) ? [$category] : $category;
|
||||
$this->extractor = $extractor;
|
||||
|
||||
$this->username = Database::getConfigKey("tracker.animebytes.auth.username", null);
|
||||
$this->passkey = trim(file_get_contents(Database::getConfigKey("tracker.animebytes.auth.passkeyFile", null)));
|
||||
$this->loggedIn = ($this->username != null and $this->passkey != null);
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "AnimeBytes";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
if(!$this->loggedIn){
|
||||
return [];
|
||||
}
|
||||
|
||||
$url = Database::getConfigKey("tracker.animebytes.scrapeUrl", "https://animebytes.tv/scrape.php");
|
||||
|
||||
$params = [
|
||||
"username" => $this->username,
|
||||
"torrent_pass" => $this->passkey,
|
||||
"type" => "anime",
|
||||
"filter_cat[1]" => "1",
|
||||
"searchstr" => "",
|
||||
"actions" => "advanced",
|
||||
"search_type" => "title",
|
||||
"year" => "",
|
||||
"year2" => "",
|
||||
"tags" => "-lolicon",
|
||||
"tags_type" => "1",
|
||||
"sort" => "time_added",
|
||||
"way" => "desc",
|
||||
"hentai" => "0",
|
||||
"showhidden" => "0",
|
||||
"anime[tv_series]" => "1",
|
||||
"anime[bd_special]" => "1",
|
||||
"anime[dvd_special]" => "1",
|
||||
//"anime[tv_special]" => "1",
|
||||
"anime[ova]" => "1",
|
||||
"anime[ona]" => "1",
|
||||
"releasegroup" => "",
|
||||
"epcount" => "",
|
||||
"epcount2" => "",
|
||||
"artbooktitle" => "",
|
||||
"containers[avi]" => "1",
|
||||
"containers[mkv]" => "1",
|
||||
"containers[mp4]" => "1",
|
||||
"containers[ogm]" => "1",
|
||||
"containers[wmv]" => "1",
|
||||
"containers[mpg]" => "1",
|
||||
"containers[mpeg]" => "1",
|
||||
];
|
||||
|
||||
foreach($this->category as $category){
|
||||
$params["animetrans[$category]"] = "1";
|
||||
}
|
||||
|
||||
if($this->uploader !== null){
|
||||
$params["releasegroup"] = $this->uploader;
|
||||
}
|
||||
|
||||
if($query !== null){
|
||||
$params["sort"] = "relevance";
|
||||
$params["way"] = "desc";
|
||||
$params["searchstr"] = $query;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$feed = Request::getURL($url . "?" . http_build_query($params));
|
||||
|
||||
if($feed == ""){
|
||||
return $results;
|
||||
}
|
||||
|
||||
$json = json_decode($feed, true);
|
||||
if(isset($json["error"])){
|
||||
Downloader::log("[ERROR] " . $json["error"], $this->getName());
|
||||
return $results;
|
||||
}
|
||||
|
||||
if(!isset($json["Groups"])){
|
||||
return $results;
|
||||
}
|
||||
|
||||
foreach($json["Groups"] as $item){
|
||||
$title = $item["SeriesName"];
|
||||
$aid = "";
|
||||
|
||||
$rating = null;//$item["Rating"] //not implemented on AB;
|
||||
$votes = $item["Votes"];
|
||||
|
||||
foreach($item["Links"] as $link){
|
||||
if(preg_match("#anidb\\.net/a([0-9]+)$#", $link, $matches) > 0){
|
||||
$aid = intval($matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($item["Torrents"] as $torrent){
|
||||
$downloadInfo = $torrent["Property"];
|
||||
|
||||
$downloadInfoLink = "https://animebytes.tv/torrents.php?id=". $item["ID"] ."&torrentid=" . $torrent["ID"];
|
||||
$downloadLink = $torrent["Link"];
|
||||
|
||||
$releaseInfo = [
|
||||
//"group" => $item,
|
||||
"title" => $title,
|
||||
"aid" => $aid,
|
||||
"info" => $downloadInfo,
|
||||
"edition" => isset($torrent["EditionData"]["EditionTitle"]) ? $torrent["EditionData"]["EditionTitle"] : null,
|
||||
];
|
||||
|
||||
$release = $this->extractor->extractInformation(http_build_query($releaseInfo), $this->category);
|
||||
|
||||
if($release === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$release->setInfoLink($downloadInfoLink);
|
||||
$release->setDownloadLink($downloadLink);
|
||||
$release->setUploadDate(\DateTime::createFromFormat("Y-m-d H:i:s", $torrent["UploadTime"], new \DateTimeZone("UTC"))->getTimestamp());
|
||||
|
||||
$release->setSeeds((int) $torrent["Seeders"]);
|
||||
|
||||
$release->setProperty("animebytes:rating", $rating);
|
||||
$release->setProperty("animebytes:votes", $votes);
|
||||
|
||||
if(isset(Database::getConfigKey("tracker.animebytes.trusted", [])[$release->getGroup()])){
|
||||
$release->setTrusted(Database::getConfigKey("tracker.animebytes.trusted", [])[$release->getGroup()]);
|
||||
}else if(Database::getConfigKey("tracker.animebytes.goldlist.trusted", false)){
|
||||
$items = explode("-", strtolower($release->getGroup()));
|
||||
foreach(Database::getConfigKey("filter.groups.goldlist", []) as $gold){
|
||||
if($release->isTrusted()){
|
||||
break;
|
||||
}
|
||||
|
||||
if(strtolower($release->getGroup()) == strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
|
||||
foreach($items as $i){
|
||||
if($i === strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.animebytes.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.animebytes.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.animebytes.unknown.source.default", "TV")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaa.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.animebytes.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.animebytes.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.animebytes.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.animebytes.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.animebytes.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function setUploader($uploader){
|
||||
$this->uploader = ($uploader === null ? -1 : $uploader);
|
||||
}
|
||||
}
|
||||
|
||||
|
82
src/Animarr/Source/HorribleSubs.php
Normal file
82
src/Animarr/Source/HorribleSubs.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class HorribleSubs implements Source{
|
||||
|
||||
private $extractor;
|
||||
|
||||
public function __construct(SceneExtractor $extractor){
|
||||
$this->extractor = $extractor;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "HorribleSubs";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = Database::getConfigKey("tracker.hs.rss.baseurl", "https://horriblesubs.info/rss.php?res=all");
|
||||
|
||||
$results = [];
|
||||
|
||||
$feed = Request::getURL($url);
|
||||
if($feed == ""){
|
||||
return $results;
|
||||
}
|
||||
$xml = new \SimpleXMLElement($feed);
|
||||
foreach($xml->channel->item as $item){
|
||||
$title = (string) $item->title;
|
||||
$release = $this->extractor->extractInformation($title);
|
||||
if($release !== null){
|
||||
$release->setInfoLink((string) $item->link);
|
||||
$release->setDownloadLink((string) $item->link);
|
||||
$release->setUploadDate(\DateTime::createFromFormat(\DateTime::RSS, (string) $item->pubDate)->getTimestamp());
|
||||
|
||||
$release->setSeeds(Database::getConfigKey("filter.minseeds", 3));
|
||||
$release->setTrusted(true);
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hs.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.hs.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.hs.unknown.source.default", "WEB")));
|
||||
}
|
||||
}
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hs.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.hs.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.hs.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hs.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.hs.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.hs.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
|
109
src/Animarr/Source/HorribleSubsOld.php
Normal file
109
src/Animarr/Source/HorribleSubsOld.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class HorribleSubsOld implements Source{
|
||||
|
||||
private $extractor;
|
||||
|
||||
public function __construct(SceneExtractor $extractor){
|
||||
$this->extractor = $extractor;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "HorribleSubsOld";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = Database::getConfigKey("tracker.hsold.feed.baseurl", "https://horriblesubs.info/lib/latest.php?mode=batch");
|
||||
|
||||
$results = [];
|
||||
|
||||
$offset = 0;
|
||||
|
||||
do{
|
||||
$lastCount = count($results);
|
||||
$feed = Request::getURL($url . "&nextid=" . $offset);
|
||||
if($feed == ""){
|
||||
return $results;
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
@$dom->loadHTML($feed);
|
||||
foreach($dom->getElementsByTagName("tr") as $item){
|
||||
if($item->getElementsByTagName("td")->item(0)->getAttribute("class") != "dl-label"){
|
||||
continue;
|
||||
}
|
||||
|
||||
$title = "[HorribleSubs] " . $item->getElementsByTagName("td")->item(0)->textContent;
|
||||
$magnetLink = null;
|
||||
|
||||
foreach($item->getElementsByTagName("a") as $link){
|
||||
if(strpos($link->getAttribute("href"), "magnet:") === 0){
|
||||
$magnetLink = $link->getAttribute("href");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$release = $this->extractor->extractInformation($title);
|
||||
if($magnetLink !== null and $release !== null){
|
||||
$release->setInfoLink($magnetLink);
|
||||
$release->setDownloadLink($magnetLink);
|
||||
|
||||
$release->setSeeds(Database::getConfigKey("filter.minseeds", 3));
|
||||
$release->setTrusted(true);
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hsold.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.hsold.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.hsold.unknown.source.default", "WEB")));
|
||||
}
|
||||
}
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hsold.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.hsold.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.hsold.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.hsold.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.hsold.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.hsold.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release instanceof MultiRelease and count($release->getContents()) <= 0 and Database::getConfigKey("magnet.unknownRelease.wait", false)){
|
||||
Torrent::queueMagnetTorrent($magnetLink, Database::getConfigKey("torrent.cache.folder", null));
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
|
||||
++$offset;
|
||||
}while(($maxResults === -1 or count($results) < $maxResults) and $lastCount !== count($results));
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
|
133
src/Animarr/Source/Nyaa.php
Executable file
133
src/Animarr/Source/Nyaa.php
Executable file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
|
||||
class Nyaa implements Source{
|
||||
|
||||
const CATEGORY_ANIME_RAW = "1_11";
|
||||
const CATEGORY_ANIME_ENGLISH = "1_37";
|
||||
const CATEGORY_ANIME_NON_ENGLISH = "1_38";
|
||||
|
||||
const FILTER_ALL = 0;
|
||||
const FILTER_REMAKES = 1;
|
||||
const FILTER_TRUSTED_ONLY = 2;
|
||||
const FILTER_APLUS_ONLY = 3;
|
||||
|
||||
private $category;
|
||||
private $filter;
|
||||
private $uploader = -1;
|
||||
private $extractor;
|
||||
|
||||
public function __construct(SceneExtractor $extractor, $category = self::CATEGORY_ANIME_ENGLISH, $filter = self::FILTER_ALL){
|
||||
$this->category = $category;
|
||||
$this->filter = $filter;
|
||||
$this->extractor = $extractor;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Nyaa";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = Database::getConfigKey("tracker.nyaa.rss.baseurl", "https://www.nyaa.se/?page=rss");
|
||||
|
||||
if($query !== null){
|
||||
$url .= "&term=". urlencode($query);
|
||||
}
|
||||
|
||||
$url .= "&cats={$this->category}";
|
||||
$url .= "&filter={$this->filter}";
|
||||
|
||||
if($this->uploader !== -1){
|
||||
$url .= "&uploader={$this->uploader}";
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$offset = 1;
|
||||
|
||||
do{
|
||||
$lastCount = count($results);
|
||||
//Downloader::log("Fetch $url . \"&offset={$offset}\"", "NYAA");
|
||||
$feed = file_get_contents($url . "&offset={$offset}");
|
||||
if($feed == ""){
|
||||
break;
|
||||
}
|
||||
$xml = new \SimpleXMLElement($feed);
|
||||
foreach($xml->channel->item as $item){
|
||||
$title = (string) $item->title;
|
||||
|
||||
$release = $this->extractor->extractInformation($title);
|
||||
if($release !== null){
|
||||
$release->setInfoLink((string) $item->guid);
|
||||
$release->setDownloadLink((string) $item->link);
|
||||
$release->setUploadDate(\DateTime::createFromFormat(\DateTime::RSS, (string) $item->pubDate)->getTimestamp());
|
||||
|
||||
if(preg_match("/^(?P<seeds>[0-9]+) seeder\\(s\\), (?P<leechs>[0-9]+) leecher\\(s\\), (?P<downloads>[0-9]+) download\\(s\\) \\- (?P<size>[0-9\\.]+ [TGMK]iB)(?P<extra>.*)$/", (string) $item->description, $matches) > 0){
|
||||
$release->setSeeds((int) $matches["seeds"]);
|
||||
foreach(explode("-", $matches["extra"]) as $w){
|
||||
$w = trim($w);
|
||||
if($w === "Trusted"){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
if($w === "A+"){
|
||||
$release->setBest(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaa.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.nyaa.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.nyaa.unknown.source.default", "TV")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaa.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.nyaa.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.nyaa.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaa.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.nyaa.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.nyaa.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
++$offset;
|
||||
}while(($maxResults === -1 or count($results) < $maxResults) and $lastCount !== count($results));
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function setUploader($uploader){
|
||||
$this->uploader = ($uploader === null ? -1 : $uploader);
|
||||
}
|
||||
}
|
||||
|
||||
|
167
src/Animarr/Source/NyaaSi.php
Normal file
167
src/Animarr/Source/NyaaSi.php
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Request;
|
||||
|
||||
class NyaaSi implements Source{
|
||||
|
||||
const CATEGORY_ANIME_RAW = "1_4";
|
||||
const CATEGORY_ANIME_ENGLISH = "1_2";
|
||||
const CATEGORY_ANIME_NON_ENGLISH = "1_3";
|
||||
|
||||
const FILTER_ALL = 0;
|
||||
const FILTER_REMAKES = 1;
|
||||
const FILTER_TRUSTED_ONLY = 2;
|
||||
|
||||
private $category;
|
||||
private $filter;
|
||||
private $uploader = null;
|
||||
private $extractor;
|
||||
|
||||
public function __construct(SceneExtractor $extractor, $category = self::CATEGORY_ANIME_ENGLISH, $filter = self::FILTER_ALL){
|
||||
$this->category = $category;
|
||||
$this->filter = $filter;
|
||||
$this->extractor = $extractor;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "NyaaSi";
|
||||
}
|
||||
|
||||
public function getTorrentAPIInformation($id){
|
||||
//TODO: make this work
|
||||
$json = Request::getAuthURL(Database::getConfigKey("tracker.nyaasi.api.baseurl", "https://nyaa.si/api/") ."info/$id", Database::getConfigKey("tracker.nyaasi.api.auth.username", null), trim(file_get_contents(Database::getConfigKey("tracker.nyaasi.api.auth.passwordFile", null)), "\r\n\t"));
|
||||
$data = json_decode($json, true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = Database::getConfigKey("tracker.nyaasi.rss.baseurl", "https://nyaa.si/?page=rss");
|
||||
|
||||
if($query !== null){
|
||||
$url .= "&q=". urlencode($query . $extraQuery);
|
||||
}
|
||||
|
||||
$url .= "&c={$this->category}";
|
||||
$url .= "&f={$this->filter}";
|
||||
|
||||
if($this->uploader !== null){
|
||||
$url .= "&u={$this->uploader}";
|
||||
}
|
||||
|
||||
$results = [];
|
||||
|
||||
$offset = 1;
|
||||
|
||||
do{
|
||||
$lastCount = count($results);
|
||||
$feed = Request::getURL($url/* . "&offset={$offset}"*/); //TODO: add offset later
|
||||
if($feed == ""){
|
||||
break;
|
||||
}
|
||||
$xml = new \SimpleXMLElement($feed);
|
||||
foreach($xml->channel->item as $item){
|
||||
$title = (string) $item->title;
|
||||
|
||||
$release = $this->extractor->extractInformation($title);
|
||||
if($release !== null){
|
||||
$extraInfo = $item->children("nyaa", true);
|
||||
$release->setInfoLink((string) $item->guid);
|
||||
$release->setDownloadLink((string) $item->link);
|
||||
$release->setUploadDate(\DateTime::createFromFormat(\DateTime::RSS, (string) $item->pubDate)->getTimestamp());
|
||||
$release->setSeeds((string) $extraInfo->seeders);
|
||||
|
||||
/*if(preg_match("/^(?P<seeds>[0-9]+) seeder\\(s\\), (?P<leechs>[0-9]+) leecher\\(s\\), (?P<downloads>[0-9]+) download\\(s\\) \\- (?P<size>[0-9\\.]+ [TGMK]iB)(?P<extra>.*)$/", (string) $item->description, $matches) > 0){
|
||||
$release->setSeeds((int) $matches["seeds"]);
|
||||
foreach(explode("-", $matches["extra"]) as $w){
|
||||
$w = trim($w);
|
||||
if($w === "Trusted"){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
if($w === "A+"){
|
||||
$release->setBest(true);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
if(isset(Database::getConfigKey("tracker.nyaasi.trusted", [])[$release->getGroup()])){
|
||||
$release->setTrusted(Database::getConfigKey("tracker.nyaasi.trusted", [])[$release->getGroup()]);
|
||||
}else if(Database::getConfigKey("tracker.nyaasi.goldlist.trusted", false)){
|
||||
$items = explode("-", strtolower($release->getGroup()));
|
||||
foreach(Database::getConfigKey("filter.groups.goldlist", []) as $gold){
|
||||
if($release->isTrusted()){
|
||||
break;
|
||||
}
|
||||
|
||||
if(strtolower($release->getGroup()) == strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
}
|
||||
|
||||
foreach($items as $i){
|
||||
if($i === strtolower($gold)){
|
||||
$release->setTrusted(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaasi.unknown.source", [])[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.nyaasi.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.nyaasi.unknown.source.default", "TV")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaasi.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.nyaasi.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.nyaasi.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.nyaasi.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.nyaasi.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.nyaasi.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
++$offset;
|
||||
|
||||
break; //TODO: add offset later
|
||||
}while(($maxResults === -1 or count($results) < $maxResults) and $lastCount !== count($results));
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function setUploader($uploader){
|
||||
$this->uploader = ($uploader === null ? null : $uploader);
|
||||
}
|
||||
}
|
||||
|
||||
|
12
src/Animarr/Source/Source.php
Executable file
12
src/Animarr/Source/Source.php
Executable file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
interface Source{
|
||||
|
||||
public function find($query, $maxResults = -1, $extraQuery = "");
|
||||
|
||||
public function canSearch();
|
||||
|
||||
public function getName();
|
||||
}
|
37
src/Animarr/Source/SourceList.php
Normal file
37
src/Animarr/Source/SourceList.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
class SourceList implements Source{
|
||||
private $sources = [];
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$results = [];
|
||||
foreach($this->sources as $source){
|
||||
if($query !== null and !$source->canSearch()){
|
||||
continue;
|
||||
}
|
||||
if($query === null){
|
||||
\Animarr\Downloader::log("Polling " . $source->getName(), "POLL");
|
||||
}
|
||||
try{
|
||||
$results = array_merge($results, $source->find($query, $maxResults, $extraQuery));
|
||||
}catch(\Throwable $e){
|
||||
echo $e . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "SourceList";
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addSource(Source $source){
|
||||
$this->sources[] = $source;
|
||||
}
|
||||
}
|
185
src/Animarr/Source/WebFeed.php
Normal file
185
src/Animarr/Source/WebFeed.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Source;
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Extractor\SceneExtractor;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Request;
|
||||
|
||||
class WebFeed implements Source{
|
||||
|
||||
private $extractor;
|
||||
private $url;
|
||||
private $name;
|
||||
|
||||
public function __construct(SceneExtractor $extractor, $url, $name){
|
||||
$this->extractor = $extractor;
|
||||
$this->url = $url;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function canSearch(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "WebFeed " . $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param int $maxResults
|
||||
* @return Release[]
|
||||
*/
|
||||
public function find($query, $maxResults = -1, $extraQuery = ""){
|
||||
$url = $this->url;
|
||||
|
||||
$results = [];
|
||||
|
||||
$offset = 1;
|
||||
|
||||
do{
|
||||
$lastCount = count($results);
|
||||
$feed = Request::getURL($url . (Database::getConfigKey("tracker.webfeed.{$this->name}.pagination", true) ? (strpos($url, "?") !== false ? "&" : "?") . "paged=" . $offset : ""));
|
||||
|
||||
if($feed == ""){
|
||||
Downloader::log("Error getting feed!", "WebFeed");
|
||||
return $results;
|
||||
}
|
||||
$xml = new \SimpleXMLElement($feed);
|
||||
foreach($xml->channel->item as $item){
|
||||
$originalTitle = (string) $item->title;
|
||||
$title = str_replace(["–", "(TV)", "(BD)", "(DVD)"], ["-", "", "", ""], $originalTitle);
|
||||
$contents = (string) $item->asXML();
|
||||
$releases = [];
|
||||
|
||||
if(preg_match_all("/href=[\"'](magnet:[^\"']+)[\"']/", $contents, $matches) > 0){
|
||||
foreach($matches[1] as $magnet){
|
||||
$magnet = html_entity_decode($magnet);
|
||||
$props = Torrent::getMagnetProperties($magnet);
|
||||
|
||||
$torrent = Torrent::fromURL($magnet, Database::getConfigKey("torrent.cache.folder", null));
|
||||
if(isset($props["dn"])){
|
||||
$r = ["magnet" => $magnet, "hasTorrent" => $torrent !== null, "release" => $props["dn"]];
|
||||
}else{
|
||||
$r = ["magnet" => $magnet, "hasTorrent" => $torrent !== null];
|
||||
if($torrent !== null){
|
||||
$files = $torrent->getFiles();
|
||||
$r["release"] = $files[0]["path"][0];
|
||||
}
|
||||
}
|
||||
|
||||
$releases[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
if(preg_match_all("#href=[\"'](http[^\"']+)[\"']#", $contents, $matches) > 0){
|
||||
foreach($matches[1] as $tUrl){
|
||||
$tUrl = html_entity_decode($tUrl);
|
||||
if(!Torrent::isTorrentDownloadPath($tUrl)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$torrent = Torrent::fromURL($tUrl, Database::getConfigKey("torrent.cache.folder", null));
|
||||
|
||||
if($torrent !== null){
|
||||
$files = $torrent->getFiles();
|
||||
if(count($files) > 0){
|
||||
$releases[] = ["torrent" => $tUrl, "release" => $files[0]["path"][0], "hasTorrent" => true];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tUrl = trim($item->link);
|
||||
if(Torrent::isTorrentDownloadPath($tUrl)){
|
||||
$torrent = Torrent::fromURL($tUrl, Database::getConfigKey("torrent.cache.folder", null));
|
||||
|
||||
if($torrent !== null){
|
||||
$files = $torrent->getFiles();
|
||||
if(count($files) > 0){
|
||||
$releases[] = ["torrent" => $tUrl, "release" => $files[0]["path"][0], "hasTorrent" => true];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($releases as $info){
|
||||
if(!isset($info["release"])){
|
||||
if(isset($info["magnet"]) and !$info["hasTorrent"] and Database::getConfigKey("magnet.unknownRelease.wait", false)){
|
||||
Torrent::queueMagnetTorrent($info["magnet"], Database::getConfigKey("torrent.cache.folder", null));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Database::getConfigKey("tracker.webfeed.{$this->name}.modifyTitle", true)){
|
||||
$group = Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.group", $this->name);
|
||||
$releaseTitle = "[$group] $title";
|
||||
}else{
|
||||
$releaseTitle = $title;
|
||||
}
|
||||
}else{
|
||||
$releaseTitle = $info["release"];
|
||||
}
|
||||
|
||||
$release = $this->extractor->extractInformation($releaseTitle);
|
||||
|
||||
if($release !== null){
|
||||
$release->setInfoLink(trim($item->link));
|
||||
$release->setDownloadLink(isset($info["magnet"]) ? $info["magnet"] : $info["torrent"]);
|
||||
if(isset($item->pubDate)){
|
||||
$release->setUploadDate(\DateTime::createFromFormat(\DateTime::RSS, (string) $item->pubDate)->getTimestamp());
|
||||
}
|
||||
|
||||
$release->setSeeds(Database::getConfigKey("filter.minseeds", 3));
|
||||
if(isset(Database::getConfigKey("tracker.webfeed.{$this->name}.trusted", [])[$release->getGroup()])){
|
||||
$release->setTrusted(Database::getConfigKey("tracker.webfeed.{$this->name}.trusted", [])[$release->getGroup()]);
|
||||
}
|
||||
|
||||
if($release->getSource() === Release::SOURCE_UNKNOWN){
|
||||
if(strpos($originalTitle, "(TV)") !== false){
|
||||
$release->setSource(Release::SOURCE_TV);
|
||||
}else if(strpos($originalTitle, "(BD)") !== false){
|
||||
$release->setSource(Release::SOURCE_BLURAY);
|
||||
}else if(strpos($originalTitle, "(DVD)") !== false){
|
||||
$release->setSource(Release::SOURCE_DVD);
|
||||
}else if(isset(Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.source", null)[$release->getGroup()])){
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.source", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setSource(constant(Release::class . "::SOURCE_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.source.default", "WEB")));
|
||||
}
|
||||
}
|
||||
if($release->getQuality() === Release::QUALITY_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.quality", [])[$release->getGroup()])){
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.quality", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setQuality(constant(Release::class . "::QUALITY_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.quality.default", "720")));
|
||||
}
|
||||
}
|
||||
if($release->getVideoCodec() === Release::VIDEO_CODEC_UNKNOWN){
|
||||
if(isset(Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.videoCodec", [])[$release->getGroup()])){
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.videoCodec", [])[$release->getGroup()]));
|
||||
}else{
|
||||
$release->setVideoCodec(constant(Release::class . "::VIDEO_CODEC_" . Database::getConfigKey("tracker.webfeed.{$this->name}.unknown.videoCodec.default", "H264")));
|
||||
}
|
||||
}
|
||||
|
||||
if($release instanceof MultiRelease and count($release->getContents()) <= 0 and Database::getConfigKey("magnet.unknownRelease.wait", false)){
|
||||
Torrent::queueMagnetTorrent($info["magnet"], Database::getConfigKey("torrent.cache.folder", null));
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[$release->getId()] = $release;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++$offset;
|
||||
}while(Database::getConfigKey("tracker.webfeed.{$this->name}.pagination", true) and ($maxResults == -1 or count($results) < $maxResults) and $lastCount !== count($results));
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
|
84
src/Animarr/Torrent/Base32.php
Normal file
84
src/Animarr/Torrent/Base32.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Torrent;
|
||||
|
||||
/**
|
||||
* Encode in Base32 based on RFC 4648.
|
||||
* Requires 20% more space than base64
|
||||
* Great for case-insensitive filesystems like Windows and URL's (except for = char which can be excluded using the pad option for urls)
|
||||
*
|
||||
* @package default
|
||||
* @author Bryan Ruiz
|
||||
**/
|
||||
class Base32 {
|
||||
|
||||
private static $map = array(
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
||||
'=' // padding char
|
||||
);
|
||||
|
||||
private static $flippedMap = array(
|
||||
'A'=>'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7',
|
||||
'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15',
|
||||
'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23',
|
||||
'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31'
|
||||
);
|
||||
|
||||
/**
|
||||
* Use padding false when encoding for urls
|
||||
*
|
||||
* @return base32 encoded string
|
||||
* @author Bryan Ruiz
|
||||
**/
|
||||
public static function encode($input, $padding = true) {
|
||||
if(empty($input)) return "";
|
||||
$input = str_split($input);
|
||||
$binaryString = "";
|
||||
for($i = 0; $i < count($input); $i++) {
|
||||
$binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$fiveBitBinaryArray = str_split($binaryString, 5);
|
||||
$base32 = "";
|
||||
$i=0;
|
||||
while($i < count($fiveBitBinaryArray)) {
|
||||
$base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)];
|
||||
$i++;
|
||||
}
|
||||
if($padding && ($x = strlen($binaryString) % 40) != 0) {
|
||||
if($x == 8) $base32 .= str_repeat(self::$map[32], 6);
|
||||
else if($x == 16) $base32 .= str_repeat(self::$map[32], 4);
|
||||
else if($x == 24) $base32 .= str_repeat(self::$map[32], 3);
|
||||
else if($x == 32) $base32 .= self::$map[32];
|
||||
}
|
||||
return $base32;
|
||||
}
|
||||
|
||||
public static function decode($input) {
|
||||
if(empty($input)) return;
|
||||
$paddingCharCount = substr_count($input, self::$map[32]);
|
||||
$allowedValues = array(6,4,3,1,0);
|
||||
if(!in_array($paddingCharCount, $allowedValues)) return false;
|
||||
for($i=0; $i<4; $i++){
|
||||
if($paddingCharCount == $allowedValues[$i] &&
|
||||
substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false;
|
||||
}
|
||||
$input = str_replace('=','', $input);
|
||||
$input = str_split($input);
|
||||
$binaryString = "";
|
||||
for($i=0; $i < count($input); $i = $i+8) {
|
||||
$x = "";
|
||||
if(!in_array($input[$i], self::$map)) return false;
|
||||
for($j=0; $j < 8; $j++) {
|
||||
$x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$eightBits = str_split($x, 8);
|
||||
for($z = 0; $z < count($eightBits); $z++) {
|
||||
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
|
||||
}
|
||||
}
|
||||
return $binaryString;
|
||||
}
|
||||
}
|
306
src/Animarr/Torrent/Torrent.php
Executable file
306
src/Animarr/Torrent/Torrent.php
Executable file
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
namespace Animarr\Torrent;
|
||||
use Animarr\Database;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Request;
|
||||
|
||||
class Torrent{
|
||||
|
||||
private $announce;
|
||||
|
||||
private $creationDate;
|
||||
|
||||
private $info;
|
||||
|
||||
private $info_hash;
|
||||
|
||||
private $files = [];
|
||||
|
||||
public static $urls = [];
|
||||
|
||||
public static function isTorrentDownloadPath(&$url){
|
||||
$data = parse_url($url);
|
||||
|
||||
if(isset($data["scheme"]) and $data["scheme"] == "magnet"){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and substr($data["path"], -8) === ".torrent"){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#^/dl/.*$#", $data["path"]) > 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "anirena.com" or $data["host"] == "www.anirena.com") and preg_match("#^/dl/[0-9]+$#", $data["path"]) > 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "www.frozen-layer.com" or $data["host"] == "frozen-layer.com") and preg_match("#^/descargas/([0-9]+.*)$#", $data["path"], $matches) > 0){
|
||||
$url = "https://www.frozen-layer.com/descargas/".$matches[1] .".torrent";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#page=torrent&id=([0-9]+)$#", $data["path"], $matches) > 0){
|
||||
$url = "https://anidex.info/dl/".$matches[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "anidex.info" or $data["host"] == "anidex.moe") and preg_match("#/torrent/([0-9]+)$#", $data["path"], $matches) > 0){
|
||||
$url = "https://anidex.info/dl/".$matches[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
//if(isset($data["path"]) and ($data["host"] == "nyaa.se" or $data["host"] == "www.nyaa.se") and preg_match("#page=download&tid=/.*$#", $data["path"]) > 0){
|
||||
//return true;
|
||||
//}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "nyaa.si" or $data["host"] == "www.nyaa.si") and preg_match("#view/[0-9]+/torrent$#", $data["path"]) > 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and $data["host"] == "animebytes.tv" and preg_match("#torrent/[0-9]+/download#", $data["path"]) > 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "nyaa.se" or $data["host"] == "nyaa.eu" or $data["host"] == "www.nyaa.se") and preg_match("#page=(view|download)&tid=/.*$#", $data["path"], $matches) > 0){
|
||||
$url = "https://nyaa.si/view/".$matches[1]."/torrent";
|
||||
return true;
|
||||
}
|
||||
|
||||
if(isset($data["path"]) and ($data["host"] == "nyaa.si" or $data["host"] == "www.nyaa.si") and preg_match("#view/([0-9]+)(|/torrent|\\.torrent)$#", $data["path"], $matches) > 0){
|
||||
$url = "https://nyaa.si/view/".$matches[1]."/torrent";
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function isPrivateTorrent($url){
|
||||
$data = parse_url($url);
|
||||
|
||||
if(!isset($data["host"])){
|
||||
return false;
|
||||
}
|
||||
|
||||
$domains = Database::getConfigKey("tracker.domain.private", []);
|
||||
|
||||
return in_array($data["host"], $domains);
|
||||
}
|
||||
|
||||
public static function getMagnetProperties($magnetUrl){
|
||||
$data = parse_url($magnetUrl);
|
||||
|
||||
if(!isset($data["query"]) or $data["scheme"] != "magnet"){
|
||||
return [];
|
||||
}
|
||||
|
||||
parse_str($data["query"], $query);
|
||||
return $query;
|
||||
}
|
||||
|
||||
public static function getMagnetHash($magnetUrl){
|
||||
$props = self::getMagnetProperties($magnetUrl);
|
||||
if(isset($props["xt"]) and preg_match('/urn:btih:[ ]*([A-Za-z0-9]{32,40})/', $props["xt"], $matches) > 0){
|
||||
$hash = $matches[1];
|
||||
if(strlen($hash) == 32){
|
||||
$hash = bin2hex(Base32::decode($hash));
|
||||
}
|
||||
|
||||
$hash = strtoupper($hash);
|
||||
return $hash;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function queueMagnetTorrent($magnet, $cachePath = null){
|
||||
if(!Database::getConfigKey("magnet.service.enable", false) or Database::getConfigKey("magnet.service.folder", "") === ""){
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = self::getMagnetHash($magnet);
|
||||
|
||||
if($hash !== null and $cachePath !== null and file_exists($cachePath . "/". $hash . ".torrent")){
|
||||
return true;
|
||||
}
|
||||
|
||||
if($hash === null){
|
||||
Downloader::log("Failed to magnet ".$hash." to MagnetService ($magnet)", "TORRENT");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_put_contents(Database::getConfigKey("magnet.service.folder", "") . "/" . $hash . ".magnet", $magnet);
|
||||
#Downloader::log("Added magnet ".$hash." to MagnetService", "TORRENT");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getTorrent($url, $cachePath = null){
|
||||
$contents = null;
|
||||
if(strpos($url, "urn:btih:") !== false){
|
||||
$hash = self::getMagnetHash($url);
|
||||
if($hash === null){
|
||||
return "";
|
||||
}
|
||||
|
||||
$path = $cachePath . "/". $hash . ".torrent";
|
||||
if($cachePath !== null and file_exists($path)){
|
||||
$contents = file_get_contents($path);
|
||||
}else{
|
||||
self::queueMagnetTorrent($url, $cachePath);
|
||||
return null;
|
||||
}
|
||||
}else{
|
||||
$path = $cachePath . "/url_" . md5($url) . ".torrent";
|
||||
if($cachePath !== null and file_exists($path)){
|
||||
$contents = file_get_contents($path);
|
||||
}
|
||||
|
||||
if(($contents === "" and !Database::getConfigKey("torrent.cache.addEmpty", false)) or $contents === null){
|
||||
$contents = Request::getURL($url);
|
||||
if(self::isPrivateTorrent($url)){
|
||||
sleep(Database::getConfigKey("tracker.private.fetchDelay", 5));
|
||||
}
|
||||
if($cachePath !== null and strlen($contents) > 40){
|
||||
file_put_contents($path, $contents);
|
||||
}elseif($cachePath !== null and Database::getConfigKey("torrent.cache.addEmpty", false)){
|
||||
file_put_contents($path, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
public static function fromURL($url, $cachePath = null){
|
||||
if(isset(self::$urls[$url])){
|
||||
return self::$urls[$url];
|
||||
}
|
||||
|
||||
$contents = self::getTorrent($url, $cachePath);
|
||||
if($contents === null){
|
||||
return null;
|
||||
}
|
||||
|
||||
$t = new Torrent($contents);
|
||||
|
||||
$hash = $t->getInfoHash();
|
||||
|
||||
if($contents != "" and $hash != null and $cachePath !== null and !file_exists($cachePath . "/" . $hash . ".torrent")){
|
||||
file_put_contents($cachePath . "/" . $hash . ".torrent", $contents);
|
||||
}
|
||||
|
||||
if($contents == ""){
|
||||
return $t;
|
||||
}
|
||||
|
||||
return self::$urls[$url] = $t;
|
||||
}
|
||||
|
||||
public function __construct($contents){
|
||||
$c = self::bdecode($contents);
|
||||
if(!is_array($c)){
|
||||
return;
|
||||
}
|
||||
$this->announce = isset($c["announce-list"]) ? $c["announce-list"] : isset($c["announce"]) ? [[$c["announce"]]] : [];
|
||||
$this->creationDate = isset($c["creation-date"]) ? $c["creation-date"] : time();
|
||||
|
||||
if(isset($c["info"]["files"])){
|
||||
foreach($c["info"]["files"] as $file){
|
||||
//Damn padding files
|
||||
if(strpos($file["path"][0], "_____padding_file") === 0){
|
||||
continue;
|
||||
}
|
||||
array_unshift($file["path"], $c["info"]["name"]);
|
||||
$this->files[] = [
|
||||
"size" => $file["length"],
|
||||
"path" => $file["path"],
|
||||
"stringPath" => implode("/", $file["path"]),
|
||||
];
|
||||
}
|
||||
}else{
|
||||
$this->files[] = [
|
||||
"size" => $c["info"]["length"],
|
||||
"path" => [$c["info"]["name"]],
|
||||
"stringPath" => $c["info"]["name"],
|
||||
];
|
||||
}
|
||||
|
||||
$this->info = $c["info"];
|
||||
$this->info_hash = $c["info_hash"];
|
||||
}
|
||||
|
||||
public function getCreationDate(){
|
||||
return $this->creationDate;
|
||||
}
|
||||
|
||||
public function getFiles(){
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
public function getInfoHash(){
|
||||
return $this->info_hash;
|
||||
}
|
||||
|
||||
private static function bdecode($s, &$pos = 0, $depth = 0) {
|
||||
if($pos>=strlen($s)) {
|
||||
return null;
|
||||
}
|
||||
switch($s[$pos]){
|
||||
case 'd':
|
||||
$pos++;
|
||||
$retval=array();
|
||||
while (isset($s[$pos]) and $s[$pos]!='e'){
|
||||
$key=self::bdecode($s, $pos, $depth + 1);
|
||||
$origPos = $pos;
|
||||
$val=self::bdecode($s, $pos, $depth + 1);
|
||||
if ($key===null || $val===null)
|
||||
break;
|
||||
$retval[$key]=$val;
|
||||
if($key === "info" and $depth <= 1){
|
||||
$retval["info_hash"] = strtoupper(sha1(substr($s, $origPos, $pos - $origPos)));
|
||||
}
|
||||
}
|
||||
$retval["isDct"]=true;
|
||||
$pos++;
|
||||
return $retval;
|
||||
|
||||
case 'l':
|
||||
$pos++;
|
||||
$retval=array();
|
||||
while ($s[$pos]!='e'){
|
||||
$val=self::bdecode($s, $pos, $depth + 1);
|
||||
if ($val===null)
|
||||
break;
|
||||
$retval[]=$val;
|
||||
}
|
||||
$pos++;
|
||||
return $retval;
|
||||
|
||||
case 'i':
|
||||
$pos++;
|
||||
$digits=strpos($s, 'e', $pos)-$pos;
|
||||
$val=round((float)substr($s, $pos, $digits));
|
||||
$pos+=$digits+1;
|
||||
return $val;
|
||||
|
||||
// case "0": case "1": case "2": case "3": case "4":
|
||||
// case "5": case "6": case "7": case "8": case "9":
|
||||
default:
|
||||
$digits=strpos($s, ':', $pos)-$pos;
|
||||
if ($digits<0 || $digits >20)
|
||||
return null;
|
||||
$len=(int)substr($s, $pos, $digits);
|
||||
$pos+=$digits+1;
|
||||
$str=substr($s, $pos, $len);
|
||||
$pos+=$len;
|
||||
//echo "pos: $pos str: [$str] len: $len digits: $digits\n";
|
||||
return (string)$str;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
16
src/AutoLoader.php
Executable file
16
src/AutoLoader.php
Executable file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
spl_autoload_register(function($class){
|
||||
$path = __DIR__;
|
||||
|
||||
$l = explode("\\", $class);
|
||||
|
||||
foreach($l as $part){
|
||||
|
||||
$path .= DIRECTORY_SEPARATOR . $part;
|
||||
}
|
||||
|
||||
if(file_exists($path . ".php")){
|
||||
include_once($path . ".php");
|
||||
}
|
||||
});
|
128
src/ImportOldDatabase.php
Normal file
128
src/ImportOldDatabase.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
include "AutoLoader.php";
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Filter;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Source\SourceList;
|
||||
|
||||
/*
|
||||
function errHandle($errNo, $errStr, $errFile, $errLine) {
|
||||
$msg = "$errStr in $errFile on line $errLine";
|
||||
if ($errNo == E_NOTICE || $errNo == E_WARNING) {
|
||||
throw new ErrorException($msg, $errNo);
|
||||
} else {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
|
||||
set_error_handler('errHandle');
|
||||
*/
|
||||
ini_set('memory_limit', '2048M');
|
||||
setlocale(LC_CTYPE, "en_US.UTF-8");
|
||||
|
||||
chdir(__DIR__ . "/" . "..");
|
||||
|
||||
Database::parseConfig($argv[1]);
|
||||
Downloader::log("Starting config...");
|
||||
|
||||
Downloader::log("Loading Database...");
|
||||
|
||||
$user = Database::getConfigKey("database.psql.user");
|
||||
$pass = Database::getConfigKey("database.psql.password");
|
||||
$host = Database::getConfigKey("database.psql.host");
|
||||
$dbname = Database::getConfigKey("database.psql.dbname", "animarr");
|
||||
$dbnew = pg_connect("host=$host user=$user password=$pass dbname=$dbname");
|
||||
//pg_query(file_get_contents("db.sql"));
|
||||
|
||||
$dbold = new \SQLite3($argv[2]);
|
||||
|
||||
$q = $dbold->query("SELECT * FROM tracked;");
|
||||
$count = $dbold->query("SELECT COUNT(*) as count FROM tracked;")->fetchArray(SQLITE3_ASSOC)["count"];
|
||||
pg_query($dbnew, "BEGIN");
|
||||
$i = 0;
|
||||
while(($row = $q->fetchArray(SQLITE3_ASSOC)) !== false){
|
||||
echo "\rproc tracked ".(++$i)."/".$count. " " . $row["aid"];
|
||||
$sql = "INSERT INTO tracked ({keys}) VALUES ({kvalues}) ON CONFLICT DO NOTHING;";
|
||||
$keys = [];
|
||||
$kvalues = [];
|
||||
|
||||
foreach($row as $k => $value){
|
||||
$keys[] = $k;
|
||||
$kvalues[] = '$' . (count($kvalues) + 1);
|
||||
}
|
||||
pg_query_params($dbnew, str_replace(["{keys}", "{kvalues}"], [implode(", ", $keys), implode(", ", $kvalues)], $sql), $row);
|
||||
}
|
||||
$q->finalize();
|
||||
pg_query($dbnew, "COMMIT");
|
||||
|
||||
$q = $dbold->query("SELECT * FROM releases;");
|
||||
$count = $dbold->query("SELECT COUNT(*) as count FROM releases;")->fetchArray(SQLITE3_ASSOC)["count"];
|
||||
pg_query($dbnew, "BEGIN");
|
||||
$i = 0;
|
||||
while(($row = $q->fetchArray(SQLITE3_ASSOC)) !== false){
|
||||
echo "\rproc releases ".(++$i)."/".$count. " " . $row["release_id"];
|
||||
$sql = "INSERT INTO releases ({keys}) VALUES ({kvalues}) ON CONFLICT DO NOTHING;";
|
||||
$keys = [];
|
||||
$kvalues = [];
|
||||
|
||||
foreach($row as $k => $value){
|
||||
if($k === "release_data"){
|
||||
$row[$k] = base64_decode($value);
|
||||
}
|
||||
$keys[] = $k;
|
||||
$kvalues[] = '$' . (count($kvalues) + 1);
|
||||
}
|
||||
pg_query_params($dbnew, str_replace(["{keys}", "{kvalues}"], [implode(", ", $keys), implode(", ", $kvalues)], $sql), $row);
|
||||
}
|
||||
$q->finalize();
|
||||
pg_query($dbnew, "COMMIT");
|
||||
|
||||
|
||||
$q = $dbold->query("SELECT * FROM episodes_releases;");
|
||||
$count = $dbold->query("SELECT COUNT(*) as count FROM episodes_releases;")->fetchArray(SQLITE3_ASSOC)["count"];
|
||||
pg_query($dbnew, "BEGIN");
|
||||
$i = 0;
|
||||
while(($row = $q->fetchArray(SQLITE3_ASSOC)) !== false){
|
||||
echo "\rproc episodes_releases ".(++$i)."/".$count. " " . $row["release_id"];
|
||||
$sql = "INSERT INTO episodes_releases ({keys}) VALUES ({kvalues}) ON CONFLICT DO NOTHING;";
|
||||
$keys = [];
|
||||
$kvalues = [];
|
||||
|
||||
foreach($row as $k => $value){
|
||||
if($k === "release_data"){
|
||||
$row[$k] = base64_decode($value);
|
||||
}
|
||||
$keys[] = $k;
|
||||
$kvalues[] = '$' . (count($kvalues) + 1);
|
||||
}
|
||||
pg_query_params($dbnew, str_replace(["{keys}", "{kvalues}"], [implode(", ", $keys), implode(", ", $kvalues)], $sql), $row);
|
||||
}
|
||||
$q->finalize();
|
||||
pg_query($dbnew, "COMMIT");
|
||||
|
||||
$q = $dbold->query("SELECT * FROM tracked_releases;");
|
||||
$count = $dbold->query("SELECT COUNT(*) as count FROM tracked_releases;")->fetchArray(SQLITE3_ASSOC)["count"];
|
||||
pg_query($dbnew, "BEGIN");
|
||||
$i = 0;
|
||||
while(($row = $q->fetchArray(SQLITE3_ASSOC)) !== false){
|
||||
echo "\rproc tracked_releases ".(++$i)."/".$count. " " . $row["release_id"];
|
||||
$sql = "INSERT INTO tracked_releases ({keys}) VALUES ({kvalues}) ON CONFLICT DO NOTHING;";
|
||||
$keys = [];
|
||||
$kvalues = [];
|
||||
|
||||
foreach($row as $k => $value){
|
||||
if($k === "release_data"){
|
||||
$row[$k] = base64_decode($value);
|
||||
}
|
||||
$keys[] = $k;
|
||||
$kvalues[] = '$' . (count($kvalues) + 1);
|
||||
}
|
||||
pg_query_params($dbnew, str_replace(["{keys}", "{kvalues}"], [implode(", ", $keys), implode(", ", $kvalues)], $sql), $row);
|
||||
}
|
||||
$q->finalize();
|
||||
pg_query($dbnew, "COMMIT");
|
78
src/MagnetService.js
Normal file
78
src/MagnetService.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
const DHT = require('bittorrent-dht')
|
||||
const magnet = require('magnet-uri')
|
||||
const sha1 = require('sha1')
|
||||
const fetchMetadata = require('bep9-metadata-dl')
|
||||
const bencode = require('bencode')
|
||||
var fs = require('fs')
|
||||
|
||||
var magnetFolder = process.argv[2];
|
||||
var torrentFolder = process.argv[3];
|
||||
|
||||
//var uri = 'magnet:?xt=urn:btih:e3811b9539cacff680e418124272177c47477157'
|
||||
//var parsed = magnet(uri)
|
||||
|
||||
//console.log(parsed.infoHash) // 'e3811b9539cacff680e418124272177c47477157'
|
||||
|
||||
const dht = new DHT({
|
||||
nodeId: sha1("AnimarrDHTNode"), // 160-bit DHT node ID (Buffer or hex string, default: randomly generated)
|
||||
bootstrap: [
|
||||
"router.bittorrent.com:6881",
|
||||
"router.utorrent.com:6881",
|
||||
"router.bitcomet.com:6881",
|
||||
"dht.aelitis.com:6881",
|
||||
"dht.transmissionbt.com:6881",
|
||||
"dht.libtorrent.org:25401"
|
||||
],
|
||||
host: false,
|
||||
concurrency: 200
|
||||
})
|
||||
|
||||
dht.listen(function () {
|
||||
console.log('[MagnetService] DHT node started!')
|
||||
dht.on('ready', function () {
|
||||
console.log('[MagnetService] Bootstrap completed')
|
||||
})
|
||||
})
|
||||
|
||||
dht.on('peer', function (peer, infoHash, from) {
|
||||
//console.log('found potential peer ' + peer.host + ':' + peer.port + ' through ' + from.address + ':' + from.port + ' for ' + infoHash.toString('hex'))
|
||||
})
|
||||
|
||||
|
||||
updateFunction = function(){
|
||||
fs.readdir(magnetFolder, function(err, files){
|
||||
if(err){
|
||||
return;
|
||||
}
|
||||
|
||||
files.forEach( function( file, index ) {
|
||||
if(file.charAt(0) == "."){
|
||||
return;
|
||||
}
|
||||
|
||||
var magnetFile = magnetFolder + "/" + file;
|
||||
var magnetContents = fs.readFileSync(magnetFile).toString();
|
||||
var parsed = magnet(magnetContents);
|
||||
//console.log('[MagnetService] Added ' + parsed.infoHash);
|
||||
|
||||
fs.unlinkSync(magnetFile);
|
||||
|
||||
fetchMetadata(parsed.infoHash, { maxConns: 200, fetchTimeout: 30000, socketTimeout: 5000, dht}).then(metadata => {
|
||||
var contents = bencode.encode(metadata);
|
||||
var infoContents = bencode.encode(metadata.info);
|
||||
var hash = sha1(infoContents).toUpperCase();
|
||||
console.log('[MagnetService] Fetched ' + hash + ' (' + metadata.info.name + ')!');
|
||||
fs.writeFile(torrentFolder + "/" + hash + ".torrent", contents, function(err) {});
|
||||
}).catch(err => {
|
||||
|
||||
});
|
||||
} );
|
||||
});
|
||||
};
|
||||
|
||||
setInterval(updateFunction, 10000)
|
||||
|
||||
updateFunction();
|
||||
|
||||
|
||||
|
95
web/add.php
Executable file
95
web/add.php
Executable file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
printHeader("Add new show");
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$simple = isset($_GET["simple"]) ? $_GET["simple"] == 1 : false;
|
||||
$clearAll = isset($_GET["clear"]) ? $_GET["clear"] == 1 : false;
|
||||
$aid = intval($_GET["aid"]);
|
||||
$info = $aniDB->getAnime($aid);
|
||||
if($info === null){
|
||||
header("Location: add.php");
|
||||
die();
|
||||
}
|
||||
if($database->isTracking($aid) and !$simple){
|
||||
if($clearAll){
|
||||
$database->clearEpisodes($aid);
|
||||
}
|
||||
$database->clearTrackedEpisodes($aid);
|
||||
}
|
||||
|
||||
$database->addTrack($aid);
|
||||
$database->updateTrackEpisodes($aid, $simple ? 0 : -1); //Force
|
||||
header("Location: show.php?aid=$aid");
|
||||
die();
|
||||
}
|
||||
|
||||
$showName = trim(isset($_GET["showName"]) ? $_GET["showName"] : ""); //XSS
|
||||
|
||||
$suggestions = [];
|
||||
|
||||
if($showName !== (string)((int)$showName) and $showName != ""){
|
||||
$suggestions = $aniDB->getAllMatches($showName);
|
||||
}else if($showName === (string)((int)$showName)){
|
||||
$suggestions = [
|
||||
(int)$showName => 0
|
||||
];
|
||||
/*$data = $aniDB->getAll();
|
||||
ksort($data);
|
||||
$start = (int) $showName;
|
||||
foreach($data as $aid => $info){
|
||||
if($aid >= $start){
|
||||
|
||||
$suggestions[$aid] = $aid - $start;
|
||||
|
||||
if($aid <= $aid - 100){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<form method="GET" action="add.php">
|
||||
<div class="form-group">
|
||||
<label for="shownName">Show name</label>
|
||||
<input type="text" class="form-control" id="showName" name="showName" value="<?php echo $showName;?>"/>
|
||||
<label for="matchType">Match Limit</label>
|
||||
<select name="matchType" id="matchType">
|
||||
<option value="100" <?php echo ((isset($_GET["matchType"]) and $_GET["matchType"] == 100) ? "selected" : "");?>>Any Match</option>
|
||||
<option value="0" <?php echo ((isset($_GET["matchType"]) and $_GET["matchType"] == 0) ? "selected" : "");?>>Exact Match</option>
|
||||
<option value="5" <?php echo ((isset($_GET["matchType"]) and $_GET["matchType"] == 5) ? "selected" : "");?>>Close Match</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Search</button>
|
||||
</form>
|
||||
<?php
|
||||
if(count($suggestions) > 0){
|
||||
?>
|
||||
<table class="table table-striped table-hover">
|
||||
<tr><th>AniDB #</th><th>Show Info</th><th>Match value</th></tr>
|
||||
|
||||
<?php
|
||||
foreach($suggestions as $aid => $lev){
|
||||
if(isset($_GET["matchType"]) and $_GET["matchType"] < $lev){
|
||||
continue;
|
||||
}
|
||||
$info = $aniDB->getAnime($aid);
|
||||
$xml = new \SimpleXMLElement($aniDB->getAnimeAPI($aid));
|
||||
$type = strval($xml->type);
|
||||
if($type !== "" and $type !== "TV Series" and $type !== "ONA" and $type !== "OVA" and $type !== "Web"){
|
||||
continue;
|
||||
}
|
||||
$imgUrl = "https://img7.anidb.net/pics/anime/" . strval($xml->picture);
|
||||
echo "<tr><td><a href=\"https://anidb.net/perl-bin/animedb.pl?show=anime&aid=$aid\" target=\"_blank\"><img src=\"$imgUrl\" width=120/><br/>$aid</a></td><td><a href=\"add.php?aid=$aid\" style=\"font-size: 150%;\">".$info["title"]." [$type ".strval($xml->startdate)."] </a><br/>".strval($xml->description)."</td><td>$lev</td></tr>";
|
||||
}
|
||||
|
||||
?>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
printFooter();
|
BIN
web/bg.jpg
Normal file
BIN
web/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
117
web/common.php
Executable file
117
web/common.php
Executable file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
include __DIR__ . "/../src/AutoLoader.php";
|
||||
|
||||
|
||||
use Animarr\Database;
|
||||
use Animarr\Filter;
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Release\MultiRelease;
|
||||
use Animarr\Downloader;
|
||||
use Animarr\Torrent\Torrent;
|
||||
|
||||
|
||||
define("NO_LOG", true);
|
||||
chdir(__DIR__ . "/" . "..");
|
||||
|
||||
Database::parseConfig("config_".$_SERVER["SERVER_PORT"].".ini");
|
||||
|
||||
$aniDBPath = Database::getConfigKey("database.anidb", "/tmp/anidb.cache.dat");
|
||||
|
||||
$aniDB = new \Animarr\AniDB($aniDBPath, false);
|
||||
|
||||
$database = new Database($aniDB);
|
||||
foreach($database->getAlternateTitles() as $t){
|
||||
$aniDB->addEntryName($t["aid"], $t["language"], $t["title"], $t["type"]);
|
||||
}
|
||||
|
||||
|
||||
function getURLName(&$url){
|
||||
if($url == ""){
|
||||
return "";
|
||||
}
|
||||
|
||||
if(preg_match("#nyaa\\.se/.*tid=([0-9]+)#", $url, $matches) > 0){
|
||||
return "Nyaa#" . $matches[1];
|
||||
}
|
||||
|
||||
if(preg_match("#anidex\\.(info|moe)/.*id=([0-9]+)#", $url, $matches) > 0){
|
||||
return "AniDex#" . $matches[2];
|
||||
}
|
||||
|
||||
if(preg_match("#anidex\\.(info|moe)/dl/([0-9]+)#", $url, $matches) > 0){
|
||||
$url = "https://anidex.info/torrent/" . $matches[1];
|
||||
return "AniDex#" . $matches[2];
|
||||
}
|
||||
|
||||
//TODO: Remove this
|
||||
if(preg_match("#^\\?page=torrent&id=([0-9]+)#", $url, $matches) > 0){
|
||||
$url = "https://anidex.info/?page=torrent&id=" . $matches[1];
|
||||
return "AniDex#" . $matches[1];
|
||||
}
|
||||
|
||||
if(strpos($url, "urn:btih:") !== false){
|
||||
return "Magnet#<small>" . Torrent::getMagnetHash($url) . "</small>";
|
||||
}
|
||||
|
||||
if(preg_match("#nyaa\\.si/.*view/([0-9]+)#", $url, $matches) > 0){
|
||||
return "NyaaSi#" . $matches[1];
|
||||
}
|
||||
|
||||
if(preg_match("#animebytes\\.tv/torrents\\.php\\?.*torrentid=([0-9]+)#", $url, $matches) > 0){
|
||||
return "AnimeBytes#" . $matches[1];
|
||||
}
|
||||
|
||||
return @parse_url($url)["host"];
|
||||
}
|
||||
|
||||
function printHeader($title){
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?php echo $title; ?> - Animarr</title>
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/bootstrap-theme.min.css">
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<link rel="stylesheet" href="css/base.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="background">
|
||||
<img src="bg.jpg" class="stretch" alt="" />
|
||||
</div>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">Animarr</a>
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="index.php">Home</a></li>
|
||||
<li><a href="list.php">Recent</a></li>
|
||||
<li><a href="add.php">Add</a></li>
|
||||
<li><a href="logs.php">Logs</a></li>
|
||||
<li><a href="dopoll.php" onclick="return confirm('Are you sure you want to do a full refresh?');"><span class="glyphicon glyphicon-refresh"></span></a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="base-container">
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
function printFooter(){
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
25
web/css/base.css
Executable file
25
web/css/base.css
Executable file
|
@ -0,0 +1,25 @@
|
|||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
#background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
z-index: -999;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.stretch {
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.base-container {
|
||||
padding: 40px 15px;
|
||||
}
|
||||
|
||||
.container{
|
||||
width: 90%;
|
||||
}
|
6
web/css/bootstrap-theme.min.css
vendored
Normal file
6
web/css/bootstrap-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/css/bootstrap-theme.min.css.map
Normal file
1
web/css/bootstrap-theme.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6
web/css/bootstrap.min.css
vendored
Normal file
6
web/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/css/bootstrap.min.css.map
Normal file
1
web/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
22
web/del.php
Executable file
22
web/del.php
Executable file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$aid = intval($_GET["aid"]);
|
||||
$info = $aniDB->getAnime($aid);
|
||||
if($info === null){
|
||||
header("Location: add.php");
|
||||
die();
|
||||
}
|
||||
if($database->isTracking($aid)){
|
||||
$database->clearTrackedEpisodes($aid);
|
||||
$database->stopTrack($aid);
|
||||
}
|
||||
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
|
||||
header("Location: show.php?aid=$aid");
|
||||
die();
|
8
web/dopoll.php
Executable file
8
web/dopoll.php
Executable file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
file_put_contents("dopoll.flag", "");
|
||||
header("Location: index.php");
|
||||
die();
|
||||
|
BIN
web/fonts/glyphicons-halflings-regular.eot
Normal file
BIN
web/fonts/glyphicons-halflings-regular.eot
Normal file
Binary file not shown.
288
web/fonts/glyphicons-halflings-regular.svg
Normal file
288
web/fonts/glyphicons-halflings-regular.svg
Normal file
|
@ -0,0 +1,288 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||
<missing-glyph horiz-adv-x="500" />
|
||||
<glyph horiz-adv-x="0" />
|
||||
<glyph horiz-adv-x="400" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" />
|
||||
<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="¥" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" />
|
||||
<glyph unicode=" " horiz-adv-x="650" />
|
||||
<glyph unicode=" " horiz-adv-x="1300" />
|
||||
<glyph unicode=" " horiz-adv-x="650" />
|
||||
<glyph unicode=" " horiz-adv-x="1300" />
|
||||
<glyph unicode=" " horiz-adv-x="433" />
|
||||
<glyph unicode=" " horiz-adv-x="325" />
|
||||
<glyph unicode=" " horiz-adv-x="216" />
|
||||
<glyph unicode=" " horiz-adv-x="216" />
|
||||
<glyph unicode=" " horiz-adv-x="162" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="72" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="325" />
|
||||
<glyph unicode="€" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" />
|
||||
<glyph unicode="₽" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" />
|
||||
<glyph unicode="−" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="⌛" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" />
|
||||
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="☁" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" />
|
||||
<glyph unicode="⛺" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " />
|
||||
<glyph unicode="✉" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" />
|
||||
<glyph unicode="✏" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" />
|
||||
<glyph unicode="" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" />
|
||||
<glyph unicode="" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" />
|
||||
<glyph unicode="" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" />
|
||||
<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" />
|
||||
<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" />
|
||||
<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" />
|
||||
<glyph unicode="" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" />
|
||||
<glyph unicode="" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" />
|
||||
<glyph unicode="" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" />
|
||||
<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" />
|
||||
<glyph unicode="" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" />
|
||||
<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" />
|
||||
<glyph unicode="" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" />
|
||||
<glyph unicode="" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" />
|
||||
<glyph unicode="" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" />
|
||||
<glyph unicode="" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" />
|
||||
<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" />
|
||||
<glyph unicode="" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
|
||||
<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" />
|
||||
<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" />
|
||||
<glyph unicode="" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" />
|
||||
<glyph unicode="" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" />
|
||||
<glyph unicode="" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" />
|
||||
<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" />
|
||||
<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" />
|
||||
<glyph unicode="" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" />
|
||||
<glyph unicode="" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" />
|
||||
<glyph unicode="" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" />
|
||||
<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
|
||||
<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" />
|
||||
<glyph unicode="" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" />
|
||||
<glyph unicode="" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" />
|
||||
<glyph unicode="" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" />
|
||||
<glyph unicode="" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" />
|
||||
<glyph unicode="" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" />
|
||||
<glyph unicode="" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" />
|
||||
<glyph unicode="" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" />
|
||||
<glyph unicode="" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" />
|
||||
<glyph unicode="" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" />
|
||||
<glyph unicode="" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" />
|
||||
<glyph unicode="" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" />
|
||||
<glyph unicode="" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" />
|
||||
<glyph unicode="" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" />
|
||||
<glyph unicode="" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" />
|
||||
<glyph unicode="" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" />
|
||||
<glyph unicode="" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" />
|
||||
<glyph unicode="" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" />
|
||||
<glyph unicode="" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" />
|
||||
<glyph unicode="" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" />
|
||||
<glyph unicode="" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" />
|
||||
<glyph unicode="" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" />
|
||||
<glyph unicode="" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" />
|
||||
<glyph unicode="" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" />
|
||||
<glyph unicode="" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" />
|
||||
<glyph unicode="" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" />
|
||||
<glyph unicode="" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" />
|
||||
<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" />
|
||||
<glyph unicode="" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" />
|
||||
<glyph unicode="" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" />
|
||||
<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" />
|
||||
<glyph unicode="" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" />
|
||||
<glyph unicode="" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" />
|
||||
<glyph unicode="" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" />
|
||||
<glyph unicode="" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
|
||||
<glyph unicode="" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" />
|
||||
<glyph unicode="" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" />
|
||||
<glyph unicode="" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" />
|
||||
<glyph unicode="" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" />
|
||||
<glyph unicode="" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" />
|
||||
<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" />
|
||||
<glyph unicode="" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" />
|
||||
<glyph unicode="" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" />
|
||||
<glyph unicode="" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" />
|
||||
<glyph unicode="" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
|
||||
<glyph unicode="" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" />
|
||||
<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" />
|
||||
<glyph unicode="" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" />
|
||||
<glyph unicode="" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
|
||||
<glyph unicode="" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" />
|
||||
<glyph unicode="" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" />
|
||||
<glyph unicode="" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" />
|
||||
<glyph unicode="" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" />
|
||||
<glyph unicode="" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" />
|
||||
<glyph unicode="" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" />
|
||||
<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" />
|
||||
<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" />
|
||||
<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" />
|
||||
<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" />
|
||||
<glyph unicode="" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" />
|
||||
<glyph unicode="" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " />
|
||||
<glyph unicode="" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" />
|
||||
<glyph unicode="" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" />
|
||||
<glyph unicode="" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" />
|
||||
<glyph unicode="" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" />
|
||||
<glyph unicode="" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" />
|
||||
<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" />
|
||||
<glyph unicode="" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" />
|
||||
<glyph unicode="" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" />
|
||||
<glyph unicode="" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" />
|
||||
<glyph unicode="" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" />
|
||||
<glyph unicode="" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" />
|
||||
<glyph unicode="" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" />
|
||||
<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" />
|
||||
<glyph unicode="" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" />
|
||||
<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" />
|
||||
<glyph unicode="" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" />
|
||||
<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" />
|
||||
<glyph unicode="" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
|
||||
<glyph unicode="" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" />
|
||||
<glyph unicode="" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" />
|
||||
<glyph unicode="" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" />
|
||||
<glyph unicode="" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" />
|
||||
<glyph unicode="" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
|
||||
<glyph unicode="" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" />
|
||||
<glyph unicode="" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" />
|
||||
<glyph unicode="" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
|
||||
<glyph unicode="" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" />
|
||||
<glyph unicode="" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" />
|
||||
<glyph unicode="" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" />
|
||||
<glyph unicode="" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" />
|
||||
<glyph unicode="" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" />
|
||||
<glyph unicode="" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" />
|
||||
<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" />
|
||||
<glyph unicode="" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" />
|
||||
<glyph unicode="" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" />
|
||||
<glyph unicode="" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" />
|
||||
<glyph unicode="" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" />
|
||||
<glyph unicode="" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" />
|
||||
<glyph unicode="" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" />
|
||||
<glyph unicode="" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" />
|
||||
<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" />
|
||||
<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" />
|
||||
<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" />
|
||||
<glyph unicode="" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" />
|
||||
<glyph unicode="" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" />
|
||||
<glyph unicode="" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" />
|
||||
<glyph unicode="" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" />
|
||||
<glyph unicode="" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" />
|
||||
<glyph unicode="" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" />
|
||||
<glyph unicode="" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" />
|
||||
<glyph unicode="" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" />
|
||||
<glyph unicode="" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" />
|
||||
<glyph unicode="" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" />
|
||||
<glyph unicode="" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" />
|
||||
<glyph unicode="" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" />
|
||||
<glyph unicode="" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" />
|
||||
<glyph unicode="" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" />
|
||||
<glyph unicode="" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " />
|
||||
<glyph unicode="" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" />
|
||||
<glyph unicode="" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" />
|
||||
<glyph unicode="" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" />
|
||||
<glyph unicode="" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" />
|
||||
<glyph unicode="" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
|
||||
<glyph unicode="" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" />
|
||||
<glyph unicode="" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" />
|
||||
<glyph unicode="" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
|
||||
<glyph unicode="" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" />
|
||||
<glyph unicode="" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" />
|
||||
<glyph unicode="" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" />
|
||||
<glyph unicode="" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" />
|
||||
<glyph unicode="" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" />
|
||||
<glyph unicode="" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" />
|
||||
<glyph unicode="" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" />
|
||||
<glyph unicode="" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" />
|
||||
<glyph unicode="" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" />
|
||||
<glyph unicode="" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" />
|
||||
<glyph unicode="" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" />
|
||||
<glyph unicode="" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" />
|
||||
<glyph unicode="" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" />
|
||||
<glyph unicode="" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" />
|
||||
<glyph unicode="" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" />
|
||||
<glyph unicode="" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" />
|
||||
<glyph unicode="" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" />
|
||||
<glyph unicode="" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" />
|
||||
<glyph unicode="" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" />
|
||||
<glyph unicode="" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" />
|
||||
<glyph unicode="" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" />
|
||||
<glyph unicode="" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" />
|
||||
<glyph unicode="" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" />
|
||||
<glyph unicode="" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" />
|
||||
<glyph unicode="" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" />
|
||||
<glyph unicode="🔑" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
|
||||
<glyph unicode="🚪" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
|
||||
</font>
|
||||
</defs></svg>
|
After Width: | Height: | Size: 106 KiB |
BIN
web/fonts/glyphicons-halflings-regular.ttf
Normal file
BIN
web/fonts/glyphicons-halflings-regular.ttf
Normal file
Binary file not shown.
BIN
web/fonts/glyphicons-halflings-regular.woff
Normal file
BIN
web/fonts/glyphicons-halflings-regular.woff
Normal file
Binary file not shown.
BIN
web/fonts/glyphicons-halflings-regular.woff2
Normal file
BIN
web/fonts/glyphicons-halflings-regular.woff2
Normal file
Binary file not shown.
23
web/get.php
Normal file
23
web/get.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Downloader;
|
||||
|
||||
if(!isset($_GET["release_id"])){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
$release_id = preg_replace("[^a-fA-f0-9]", "", $_GET["release_id"]);
|
||||
$release = $database->getRelease($release_id);
|
||||
if($release === null){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
|
||||
$downloader = new Downloader($database);
|
||||
$downloader->forceAddToDownload($release->getParent() !== null ? $release->getParent() : $release);
|
||||
$downloader->handleNewDownloads();
|
||||
header("Location: index.php");
|
||||
die();
|
136
web/groups.php
Executable file
136
web/groups.php
Executable file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
|
||||
$aid = 0;
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$aid = intval($_GET["aid"]);
|
||||
}
|
||||
|
||||
$info = $aniDB->getAnime($aid);
|
||||
if($info === null){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
$preferList = $database->getTrackGroups($aid);
|
||||
|
||||
|
||||
if(isset($_GET["add"]) and !in_array($_GET["add"], $preferList)){
|
||||
$preferList[] = $_GET["add"];
|
||||
$database->saveTrackGroups($aid, $preferList);
|
||||
}elseif(isset($_GET["del"]) and in_array($_GET["del"], $preferList)){
|
||||
foreach($preferList as $k => $g){
|
||||
if($g === $_GET["del"]){
|
||||
unset($preferList[$k]);
|
||||
}
|
||||
}
|
||||
$database->saveTrackGroups($aid, $preferList);
|
||||
}
|
||||
|
||||
printHeader("Groups: " . $info["title"]);
|
||||
|
||||
echo "<h1>Groups: ".$info["title"]." [anidb-$aid]</h1>";
|
||||
echo "<p>";
|
||||
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>Group</th><th>Release #</th><th>Best Source</th><th>Best Quality</th><th>Actions</th></tr>";
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach($database->getAnimeEpisodes($aid) as $r){
|
||||
/** @var Release[] $r */
|
||||
|
||||
foreach($r as $episode => $release){
|
||||
$group = $release->getGroup();
|
||||
|
||||
if(!isset($groups[$group])){
|
||||
$groups[$group] = [
|
||||
"name" => $group,
|
||||
"trusted" => false,
|
||||
"best" => false,
|
||||
"releases" => 0,
|
||||
"source" => Release::SOURCE_UNKNOWN,
|
||||
"quality" => Release::QUALITY_UNKNOWN,
|
||||
];
|
||||
}
|
||||
|
||||
$groups[$group]["releases"]++;
|
||||
if($release->getSource() > $groups[$group]["source"]){
|
||||
$groups[$group]["source"] = $release->getSource();
|
||||
}
|
||||
|
||||
if($release->getQuality() > $groups[$group]["quality"]){
|
||||
$groups[$group]["quality"] = $release->getQuality();
|
||||
}
|
||||
|
||||
if($release->isTrusted()){
|
||||
$groups[$group]["trusted"] = true;
|
||||
}
|
||||
|
||||
if($release->isBest()){
|
||||
$groups[$group]["best"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach($groups as $group){
|
||||
if($group["name"] === "UNKNOWN" or $group["name"] == ""){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($group["trusted"]){
|
||||
$name = "<span class=\"label label-success\">".$group["name"]."</span>";
|
||||
}else{
|
||||
$name = "<span class=\"label label-default\">".$group["name"]."</span>";
|
||||
}
|
||||
|
||||
|
||||
$source = $group["source"];
|
||||
if($source === Release::SOURCE_BLURAY){
|
||||
$source = "<span class=\"label label-primary\">BLURAY</span>";
|
||||
}elseif($source === Release::SOURCE_DVD){
|
||||
$source = "<span class=\"label label-success\">DVD</span>";
|
||||
}elseif($source === Release::SOURCE_LASERDISC){
|
||||
$source = "<span class=\"label label-success\">LD</span>";
|
||||
}elseif($source === Release::SOURCE_TV){
|
||||
$source = "<span class=\"label label-info\">TV</span>";
|
||||
}elseif($source === Release::SOURCE_WEB){
|
||||
$source = "<span class=\"label label-default\">WEB</span>";
|
||||
}else{
|
||||
$source = "<span class=\"label label-default\">$source</span>";
|
||||
}
|
||||
|
||||
$quality = $group["quality"];
|
||||
if($quality === Release::QUALITY_1080){
|
||||
$quality = "<span class=\"label label-primary\">1080p</span>";
|
||||
}elseif($quality === Release::QUALITY_720){
|
||||
$quality = "<span class=\"label label-success\">720p</span>";
|
||||
}else{
|
||||
$quality = "<span class=\"label label-default\">{$quality}p</span>";
|
||||
}
|
||||
|
||||
$actions = "";
|
||||
|
||||
if(in_array($group["name"], $preferList)){
|
||||
$actions .= '<a href="groups.php?aid='.$aid.'&del='.urlencode($group["name"]).'"><span class="glyphicon glyphicon-remove text-danger"></span></a>';
|
||||
}else{
|
||||
$actions .= '<a href="groups.php?aid='.$aid.'&add='.urlencode($group["name"]).'"><span class="glyphicon glyphicon-plus text-success"></span></a>';
|
||||
}
|
||||
|
||||
echo "<tr class=\"".($group["best"] ? "info" : ($group["trusted"] ? "success" : ""))."\"><td>$name</td><td>".$group["releases"]."</td><td>$source</td><td>$quality</td><td>$actions</td></tr>";
|
||||
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
echo "</p>";
|
||||
|
||||
printFooter();
|
37
web/index.php
Executable file
37
web/index.php
Executable file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
printHeader("Home");
|
||||
|
||||
echo "<h1>Tracked entries</h1>";
|
||||
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>AniDB #</th><th>Show Name</th><th>Info</th><th>Episodes</th></tr>";
|
||||
|
||||
|
||||
$animus = [];
|
||||
|
||||
foreach($database->getTracked() as $aid => $data){
|
||||
$info = $aniDB->getAnime($aid);
|
||||
$xml = new \SimpleXMLElement($aniDB->getAnimeAPI($aid));
|
||||
$data["xml"] = $xml;
|
||||
$data["aid"] = $aid;
|
||||
$data["anime"] = $info;
|
||||
$animus[strval($xml->startdate) ."|". $aid] = $data;
|
||||
}
|
||||
|
||||
krsort($animus, SORT_REGULAR);
|
||||
|
||||
foreach($animus as $data){
|
||||
//$imgUrl = "https://img7.anidb.net/pics/anime/" . strval($xml->picture);
|
||||
//echo "<tr><td><a href=\"https://anidb.net/perl-bin/animedb.pl?show=anime&aid=$aid\" target=\"_blank\"><img src=\"$imgUrl\" width=120/><br/>$aid</a></td><td><a href=\"add.php?aid=$aid\" style=\"font-size: 150%;\">".$info["title"]." [$type ".strval($xml->startdate)."] </a><br/>".strval($xml->description)."</td><td>$lev</td></tr>";
|
||||
echo "<tr><td>".$data["aid"]."</td><td><a href=\"show.php?aid=".$data["aid"]."\">".$data["anime"]["title"]."</a></td><td>".strval($data["xml"]->type)." (".strval($data["xml"]->startdate).")</td><td>".$data["episode_count"]."/".$data["xml"]->episodecount."</td></tr>";
|
||||
//echo '<a href="show.php?aid='.$aid.'" title="'.$info["title"].'"><img src="https://img7.anidb.net/pics/anime/'.$aid.'.jpg" width=120 /></a>';
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
printFooter();
|
7
web/js/bootstrap.min.js
vendored
Normal file
7
web/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
176
web/list.php
Normal file
176
web/list.php
Normal file
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Torrent\Torrent;
|
||||
use Animarr\Database;
|
||||
use Animarr\Release\MultiRelease;
|
||||
|
||||
$aid = null;
|
||||
$info = null;
|
||||
|
||||
$limitSpecial = false;
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$aid = intval($_GET["aid"]);
|
||||
$info = $aniDB->getAnime($aid);
|
||||
$limitSpecial = isset($_GET["specials"]) ? $_GET["specials"] == 1 : false;
|
||||
}
|
||||
|
||||
if($info !== null){
|
||||
|
||||
printHeader($info["title"]);
|
||||
|
||||
echo "<h1>".$info["title"]." <a href=\"https://anidb.net/perl-bin/animedb.pl?show=anime&aid=$aid\" target=\"_blank\">[anidb-$aid]</a></h1>";
|
||||
|
||||
}else{
|
||||
|
||||
printHeader("Recent releases");
|
||||
|
||||
echo "<h1>Recent releases</h1>";
|
||||
|
||||
}
|
||||
echo "<p>";
|
||||
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr>";
|
||||
if($aid === null){
|
||||
echo "<th>Show Name</th>";
|
||||
}
|
||||
echo "<th>Info</th><th>#</th><th style='min-width: 150px'>Name</th><th>Date</th><th>Link</th></tr>";
|
||||
|
||||
$blacklist = Database::getConfigKey("filter.groups.blacklist", []);
|
||||
|
||||
foreach($database->getRecentReleases(500, $aid, $limitSpecial ? Release::TYPE_SPECIAL : null) as $release){
|
||||
|
||||
$group = $release->getGroup();
|
||||
|
||||
if(in_array($group, $blacklist)){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($release->isTrusted()){
|
||||
$group = "<span class=\"label label-success\">$group</span>";
|
||||
}else{
|
||||
$group = "<span class=\"label label-default\">$group</span>";
|
||||
}
|
||||
|
||||
|
||||
$source = $release->getSourceString();
|
||||
if($release->getSource() === Release::SOURCE_BLURAY){
|
||||
$source = "<span class=\"label label-primary\">$source</span>";
|
||||
}elseif($release->getSource() === Release::SOURCE_DVD){
|
||||
$source = "<span class=\"label label-success\">$source</span>";
|
||||
}elseif($release->getSource() === Release::SOURCE_TV){
|
||||
$source = "<span class=\"label label-info\">$source</span>";
|
||||
}else{
|
||||
$source = "<span class=\"label label-default\">$source</span>";
|
||||
}
|
||||
|
||||
$quality = $release->getQualityString();
|
||||
if($release->getQuality() === Release::QUALITY_1080){
|
||||
$quality = "<span class=\"label label-primary\">$quality</span>";
|
||||
}elseif($release->getQuality() === Release::QUALITY_720){
|
||||
$quality = "<span class=\"label label-success\">$quality</span>";
|
||||
}else{
|
||||
$quality = "<span class=\"label label-default\">$quality</span>";
|
||||
}
|
||||
|
||||
$type = $release->getTypeString();
|
||||
if($release->getType() === Release::TYPE_SINGLE){
|
||||
$type = "<span class=\"label label-success\">$type</span>";
|
||||
}elseif($release->getType() === Release::TYPE_VOLUME or $release->getType() === Release::TYPE_BATCH){
|
||||
$type = "<span class=\"label label-primary\">$type</span>";
|
||||
}else if($release->getType() === Release::TYPE_SPECIAL){
|
||||
$type = "<span class=\"label label-warning\">$type</span>";
|
||||
}else{
|
||||
$type = "<span class=\"label label-default\">$type</span>";
|
||||
}
|
||||
|
||||
$episodes = $release->getNumber() !== -1 ? "<span class=\"label label-success\">#" . $release->getNumber() . "</span>" : "";
|
||||
if($release instanceof MultiRelease){
|
||||
$e = [];
|
||||
foreach($release->getContents() as $r){
|
||||
if($r->getType() == Release::TYPE_SINGLE and $r->getNumber() >= 0){
|
||||
$e[$r->getNumber()] = $r->getNumber();
|
||||
}
|
||||
}
|
||||
|
||||
sort($e);
|
||||
|
||||
$episodes = "";
|
||||
$lastEpisode = null;
|
||||
$lastWritten = null;
|
||||
foreach($e as $episodeNumber){
|
||||
$diff = ($episodeNumber - $lastEpisode);
|
||||
if($lastEpisode !== null and $diff <= 1){
|
||||
$lastEpisode = $episodeNumber;
|
||||
continue;
|
||||
}
|
||||
$episodes .= ($lastEpisode === null ? "" : ("-$lastEpisode, ")) . $episodeNumber;
|
||||
$lastEpisode = $episodeNumber;
|
||||
$lastWritten = $episodeNumber;
|
||||
}
|
||||
|
||||
|
||||
$diff = ($episodeNumber - $lastWritten);
|
||||
if($diff > 0){
|
||||
$episodes .= ($lastWritten === null ? "" : ("-$lastEpisode"));
|
||||
}
|
||||
}
|
||||
|
||||
$diff = time() - $release->getUploadDate();
|
||||
$hour = 3600;
|
||||
$day = $hour * 24;
|
||||
$week = $day * 7;
|
||||
|
||||
$date = "";
|
||||
|
||||
if($diff >= $week){
|
||||
$weeks = floor($diff / $week);
|
||||
$date .= "{$weeks}w ";
|
||||
$diff -= $weeks * $week;
|
||||
}
|
||||
|
||||
if($diff >= $day){
|
||||
$days = floor($diff / $day);
|
||||
$date .= "{$days}d ";
|
||||
$diff -= $days * $day;
|
||||
}
|
||||
|
||||
if($diff >= $hour){
|
||||
$hours = floor($diff / $hour);
|
||||
$date .= "{$hours}h ";
|
||||
$diff -= $hours * $hour;
|
||||
}
|
||||
|
||||
$date .= floor($diff / 60) . "m ago";
|
||||
|
||||
$url = $release->getInfoLink();
|
||||
$urlName = getURLName($url);
|
||||
if(strlen($urlName) > 23){
|
||||
$urlName = substr($urlName, 0, 20) . "...";
|
||||
}
|
||||
echo "<tr class=\"".($release->isBest() ? "info" : ($release->isTrusted() ? "success" : ""))."\">";
|
||||
$i = $aniDB->matchRelease($release);
|
||||
if($aid === null){
|
||||
echo "<td><small><a href=\"list.php?aid=".$i["aid"]."\">".$i["title"]."</a></small></td>";
|
||||
}else if ($i !== null and $aid != $i["aid"]){
|
||||
$database->matchRelease($release, true);
|
||||
}
|
||||
echo "<td>$group $type $source $quality</td>";
|
||||
echo "<td>$episodes</td>";
|
||||
echo "<td><small><code>".$release->getOriginalTitle()."</code> <a href=\"release.php?release_id=".$release->getId()."\"><span class=\"glyphicon glyphicon-info-sign\"></span></a></small></td>";
|
||||
echo "<td>$date</td>";
|
||||
echo "<td><a href=\"".$url."\" target=\"_blank\">".$urlName."</a> <a href=\"".$release->getDownloadLink()."\"><span class=\"glyphicon ".(strpos($release->getDownloadLink(), "magnet:") !== false ? "glyphicon-magnet" : "glyphicon-circle-arrow-down") ."\"></span></a> <a href=\"get.php?release_id=".$release->getId()."\"><span class=\"glyphicon glyphicon-plus\"></span></a></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
echo "</p>";
|
||||
|
||||
printFooter();
|
60
web/logs.php
Executable file
60
web/logs.php
Executable file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
printHeader("Logs");
|
||||
|
||||
echo "<h1>Logs</h1>";
|
||||
|
||||
|
||||
echo "<pre>";
|
||||
$count = 5000;
|
||||
|
||||
function tailCustom($filepath, $lines = 1, $adaptive = true) {
|
||||
// Open file
|
||||
$f = @fopen($filepath, "rb");
|
||||
if ($f === false) return false;
|
||||
// Sets buffer size, according to the number of lines to retrieve.
|
||||
// This gives a performance boost when reading a few lines from the file.
|
||||
if (!$adaptive) $buffer = 4096;
|
||||
else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));
|
||||
// Jump to last character
|
||||
fseek($f, -1, SEEK_END);
|
||||
// Read it and adjust line number if necessary
|
||||
// (Otherwise the result would be wrong if file doesn't end with a blank line)
|
||||
if (fread($f, 1) != "\n") $lines -= 1;
|
||||
|
||||
// Start reading
|
||||
$output = '';
|
||||
$chunk = '';
|
||||
// While we would like more
|
||||
while (ftell($f) > 0 && $lines >= 0) {
|
||||
// Figure out how far back we should jump
|
||||
$seek = min(ftell($f), $buffer);
|
||||
// Do the jump (backwards, relative to where we are)
|
||||
fseek($f, -$seek, SEEK_CUR);
|
||||
// Read a chunk and prepend it to our output
|
||||
$output = ($chunk = fread($f, $seek)) . $output;
|
||||
// Jump back to where we started reading
|
||||
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
||||
// Decrease our line counter
|
||||
$lines -= substr_count($chunk, "\n");
|
||||
}
|
||||
// While we have too many lines
|
||||
// (Because of buffer size we might have read too many)
|
||||
while ($lines++ < 0) {
|
||||
// Find first newline and remove all text before that
|
||||
$output = substr($output, strpos($output, "\n") + 1);
|
||||
}
|
||||
// Close file and return
|
||||
fclose($f);
|
||||
return trim($output);
|
||||
}
|
||||
|
||||
|
||||
echo tailCustom(\Animarr\Database::getConfigKey("log.path"), $count);
|
||||
|
||||
echo "</pre>";
|
||||
|
||||
|
||||
printFooter();
|
99
web/recentgroups.php
Normal file
99
web/recentgroups.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
|
||||
printHeader("All Groups (last 100K releases)");
|
||||
|
||||
echo "<h1>All Groups (last 100K releases)</h1>";
|
||||
echo "<p>";
|
||||
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>Group</th><th>Release #</th><th>Best Source</th><th>Best Quality</th></tr>";
|
||||
|
||||
$groups = [];
|
||||
|
||||
foreach($database->getRecentReleases(100000) as $release){
|
||||
$group = $release->getGroup();
|
||||
|
||||
if(!isset($groups[$group])){
|
||||
$groups[$group] = [
|
||||
"name" => $group,
|
||||
"trusted" => false,
|
||||
"best" => false,
|
||||
"releases" => 0,
|
||||
"source" => Release::SOURCE_UNKNOWN,
|
||||
"quality" => Release::QUALITY_UNKNOWN,
|
||||
];
|
||||
}
|
||||
|
||||
$groups[$group]["releases"]++;
|
||||
if($release->getSource() > $groups[$group]["source"]){
|
||||
$groups[$group]["source"] = $release->getSource();
|
||||
}
|
||||
|
||||
if($release->getQuality() > $groups[$group]["quality"]){
|
||||
$groups[$group]["quality"] = $release->getQuality();
|
||||
}
|
||||
|
||||
if($release->isTrusted()){
|
||||
$groups[$group]["trusted"] = true;
|
||||
}
|
||||
|
||||
if($release->isBest()){
|
||||
$groups[$group]["best"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
usort($groups, function($a, $b){
|
||||
return $b["releases"] - $a["releases"];
|
||||
});
|
||||
|
||||
foreach($groups as $group){
|
||||
if($group["name"] === "UNKNOWN" or $group["name"] == ""){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($group["trusted"]){
|
||||
$name = "<span class=\"label label-success\">".$group["name"]."</span>";
|
||||
}else{
|
||||
$name = "<span class=\"label label-default\">".$group["name"]."</span>";
|
||||
}
|
||||
|
||||
|
||||
$source = $group["source"];
|
||||
if($source === Release::SOURCE_BLURAY){
|
||||
$source = "<span class=\"label label-primary\">BLURAY</span>";
|
||||
}elseif($source === Release::SOURCE_DVD){
|
||||
$source = "<span class=\"label label-success\">DVD</span>";
|
||||
}elseif($source === Release::SOURCE_LASERDISC){
|
||||
$source = "<span class=\"label label-success\">LD</span>";
|
||||
}elseif($source === Release::SOURCE_TV){
|
||||
$source = "<span class=\"label label-info\">TV</span>";
|
||||
}elseif($source === Release::SOURCE_WEB){
|
||||
$source = "<span class=\"label label-default\">WEB</span>";
|
||||
}else{
|
||||
$source = "<span class=\"label label-default\">$source</span>";
|
||||
}
|
||||
|
||||
$quality = $group["quality"];
|
||||
if($quality === Release::QUALITY_1080){
|
||||
$quality = "<span class=\"label label-primary\">1080p</span>";
|
||||
}elseif($quality === Release::QUALITY_720){
|
||||
$quality = "<span class=\"label label-success\">720p</span>";
|
||||
}else{
|
||||
$quality = "<span class=\"label label-default\">{$quality}p</span>";
|
||||
}
|
||||
|
||||
echo "<tr class=\"".($group["best"] ? "info" : ($group["trusted"] ? "success" : ""))."\"><td>$name</td><td>".$group["releases"]."</td><td>$source</td><td>$quality</td></tr>";
|
||||
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
echo "</p>";
|
||||
|
||||
printFooter();
|
92
web/release.php
Executable file
92
web/release.php
Executable file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
|
||||
$aid = 0;
|
||||
|
||||
if(!isset($_GET["release_id"])){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
$release_id = preg_replace("[^a-fA-f0-9]", "", $_GET["release_id"]);
|
||||
$extractor = new \Animarr\Extractor\SceneExtractor($aniDB);
|
||||
|
||||
$selector = new \Animarr\Selector();
|
||||
|
||||
if(isset($_GET["episode"])){
|
||||
$episode = intval($_GET["episode"]);
|
||||
|
||||
printHeader("Release $release_id, episode $episode");
|
||||
|
||||
echo "<h1>Release $release_id, episode $episode</h1>";
|
||||
|
||||
$release = $database->getReleaseEpisode($release_id, $episode);
|
||||
if($release === null){
|
||||
var_dump($release_id, $episode);
|
||||
//header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
$debug = [];
|
||||
$extractor->extractInformation($release->getOriginalTitle(), false, $debug);
|
||||
echo "<pre>";
|
||||
var_dump($debug);
|
||||
var_dump($release->serialize());
|
||||
echo "\n";
|
||||
echo "Priority: " . $selector->calculatePriority($release) . "\n";
|
||||
echo "Pedantic priority: " . $selector->calculatePedanticPriority($release) . "\n";
|
||||
echo "Match:\n";
|
||||
var_dump($aniDB->matchRelease($release));
|
||||
echo "</pre>";
|
||||
}else{
|
||||
printHeader("Release $release_id");
|
||||
|
||||
echo "<h1>Release $release_id</h1>";
|
||||
|
||||
$release = $database->getRelease($release_id);
|
||||
if($release === null){
|
||||
var_dump($release_id);
|
||||
//header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
$debug = [];
|
||||
//var_dump($release);
|
||||
if($release instanceof \Animarr\Release\MultiRelease){
|
||||
$extractor->extractInformation($release->getOriginalTitle(), false, $debug);
|
||||
echo "<pre>";
|
||||
var_dump($debug);
|
||||
var_dump($release->serialize());
|
||||
echo "\n------------------------------------------\n";
|
||||
echo "</pre>";
|
||||
foreach ($release->getContents($extractor) as $r){
|
||||
$extractor->extractInformation($r->getOriginalTitle(), false, $debug);
|
||||
echo "<pre>";
|
||||
var_dump($debug);
|
||||
var_dump($r->serialize());
|
||||
echo "\n";
|
||||
echo "Priority: " . $selector->calculatePriority($r) . "\n";
|
||||
echo "Pedantic priority: " . $selector->calculatePedanticPriority($r) . "\n";
|
||||
echo "Match:\n";
|
||||
var_dump($aniDB->matchRelease($r));
|
||||
echo "</pre>";
|
||||
}
|
||||
}else{
|
||||
$extractor->extractInformation($release->getOriginalTitle(), false, $debug);
|
||||
echo "<pre>";
|
||||
var_dump($debug);
|
||||
var_dump($release->serialize());
|
||||
echo "\n";
|
||||
echo "Priority: " . $selector->calculatePriority($release) . "\n";
|
||||
echo "Pedantic priority: " . $selector->calculatePedanticPriority($release) . "\n";
|
||||
echo "Match:\n";
|
||||
var_dump($aniDB->matchRelease($release));
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
printFooter();
|
182
web/show.php
Executable file
182
web/show.php
Executable file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Torrent\Torrent;
|
||||
|
||||
$aid = 0;
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$aid = intval($_GET["aid"]);
|
||||
}
|
||||
|
||||
$info = $aniDB->getAnime($aid);
|
||||
if($info === null){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
|
||||
$data = $database->getTrackInfo($aid);
|
||||
$xml = new \SimpleXMLElement($aniDB->getAnimeAPI($aid, true, (isset($_GET["refresh"]) and $_GET["refresh"] == true) ? true : false));
|
||||
$imgUrl = "https://img7.anidb.net/pics/anime/" . strval($xml->picture);
|
||||
|
||||
printHeader($info["title"]);
|
||||
|
||||
echo "<table>";
|
||||
echo "<tr><td><img src=\"$imgUrl\" width=240/></td><td style=\"padding-left: 20px;\">";
|
||||
echo "<h1>".$info["title"]." <a href=\"https://anidb.net/perl-bin/animedb.pl?show=anime&aid=$aid\" target=\"_blank\">[anidb-$aid]</a></h1>";
|
||||
echo "<h2>".strval($xml->type)." (".strval($xml->startdate)."), ".$data["episode_count"]."/".$xml->episodecount." episodes</h2>";
|
||||
echo "<p><div class=\"btn-group\" role=\"group\">";
|
||||
echo "<a href=\"groups.php?aid=$aid\" type=\"button\" class=\"btn btn-info\">
|
||||
<span class=\"glyphicon glyphicon-user\" aria-hidden=\"true\"></span> Groups
|
||||
</a>";
|
||||
echo " <a href=\"list.php?aid=$aid&specials=1\" type=\"button\" class=\"btn btn-info\">
|
||||
<span class=\"glyphicon glyphicon-plus\" aria-hidden=\"true\"></span> Specials
|
||||
</a>";
|
||||
echo " <a href=\"add.php?aid=$aid&simple=1\" type=\"button\" class=\"btn btn-default\">
|
||||
<span class=\"glyphicon glyphicon-search\" aria-hidden=\"true\"></span> Force search
|
||||
</a>";
|
||||
echo " <a href=\"add.php?aid=$aid\" type=\"button\" class=\"btn btn-warning\">
|
||||
<span class=\"glyphicon glyphicon-refresh\" aria-hidden=\"true\"></span> Force refresh
|
||||
</a>";
|
||||
echo " <a href=\"show.php?aid=$aid&refresh=1\" type=\"button\" class=\"btn btn-default\">
|
||||
<span class=\"glyphicon glyphicon-refresh\" aria-hidden=\"true\"></span> Fetch metadata
|
||||
</a>";
|
||||
echo " <a href=\"titles.php?aid=$aid\" type=\"button\" class=\"btn btn-default\">
|
||||
<span class=\"glyphicon glyphicon-tag\" aria-hidden=\"true\"></span> Titles
|
||||
</a>";
|
||||
echo "</div>";
|
||||
echo " <a href=\"del.php?aid=$aid\" type=\"button\" class=\"btn btn-danger\">
|
||||
<span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\"></span> Stop tracking
|
||||
</a>";
|
||||
echo "</p><p>";
|
||||
echo "<pre>".$database->getTrackFolder($aid)."</pre>";
|
||||
echo "</p>";
|
||||
echo "</td></tr>";
|
||||
echo "</table><br/><br/>";
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>Episode #</th><th>Group</th><th>Source</th><th>Quality</th><th style='min-width: 60%;'>Name</th><th>Info</th><th>Date</th><th>Expected Date</th></tr>";
|
||||
|
||||
function getTimeDiff($diff){
|
||||
$hour = 3600;
|
||||
$day = $hour * 24;
|
||||
$week = $day * 7;
|
||||
|
||||
$date = "";
|
||||
|
||||
if($diff >= $week){
|
||||
$weeks = floor($diff / $week);
|
||||
$date .= "{$weeks}w ";
|
||||
$diff -= $weeks * $week;
|
||||
}
|
||||
|
||||
if($diff >= $day){
|
||||
$days = floor($diff / $day);
|
||||
$date .= "{$days}d ";
|
||||
$diff -= $days * $day;
|
||||
}
|
||||
|
||||
if($diff >= $hour){
|
||||
$hours = floor($diff / $hour);
|
||||
$date .= "{$hours}h ";
|
||||
$diff -= $hours * $hour;
|
||||
}
|
||||
|
||||
$date .= floor($diff / 60) . "m ago";
|
||||
return $date;
|
||||
}
|
||||
|
||||
function getEpisode(\SimpleXMLElement $xml, $number, $type = 1){
|
||||
if(isset($xml->episodes)){
|
||||
foreach ($xml->episodes->episode as $ep){
|
||||
if(strval($ep->epno->attributes()["type"]) == $type and strval($ep->epno) == $number){
|
||||
$d = [
|
||||
"episode" => strval($ep->epno),
|
||||
"airs" => strval($ep->airdate),
|
||||
"titles" => [],
|
||||
"duration" => strval($ep->length),
|
||||
];
|
||||
foreach ($ep->title as $t){
|
||||
$d["titles"][] = $tt = ["lang" => $lang = strval($t->attributes("http://www.w3.org/XML/1998/namespace")["lang"]), "title" => strval($t)];
|
||||
if($lang === "en"){
|
||||
$d["titles"]["main"] = $tt;
|
||||
}else if($lang === "x-jat" and !isset($d["titles"]["main"])){
|
||||
$d["titles"]["main"] = $tt;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($d["titles"]["main"])){
|
||||
$d["titles"]["main"] = reset($d["titles"]);
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$lastN = 0;
|
||||
|
||||
foreach($database->getTrackEpisodes($aid) as $release){
|
||||
for($n = $lastN + 1; $n < $release->getNumber(); ++$n){
|
||||
$ep = getEpisode($xml, $n);
|
||||
echo "<tr><td>$n</td><td></td><td></td><td></td><td>MISSING - ".$ep["titles"]["main"]["title"]."</td><td></td><td></td><td>".$ep["airs"]."</td></tr>";
|
||||
}
|
||||
|
||||
$ep = getEpisode($xml, $release->getNumber());
|
||||
|
||||
$group = $release->getGroup();
|
||||
if($release->isTrusted()){
|
||||
$group = "<span class=\"label label-success\">$group</span>";
|
||||
}else{
|
||||
$group = "<span class=\"label label-default\">$group</span>";
|
||||
}
|
||||
|
||||
|
||||
$source = $release->getSourceString();
|
||||
if($release->getSource() === Release::SOURCE_BLURAY){
|
||||
$source = "<span class=\"label label-primary\">$source</span>";
|
||||
}elseif($release->getSource() === Release::SOURCE_DVD){
|
||||
$source = "<span class=\"label label-success\">$source</span>";
|
||||
}elseif($release->getSource() === Release::SOURCE_TV){
|
||||
$source = "<span class=\"label label-info\">$source</span>";
|
||||
}else{
|
||||
$source = "<span class=\"label label-default\">$source</span>";
|
||||
}
|
||||
|
||||
$quality = $release->getQualityString();
|
||||
if($release->getQuality() === Release::QUALITY_1080){
|
||||
$quality = "<span class=\"label label-primary\">$quality</span>";
|
||||
}elseif($release->getQuality() === Release::QUALITY_720){
|
||||
$quality = "<span class=\"label label-success\">$quality</span>";
|
||||
}else{
|
||||
$quality = "<span class=\"label label-default\">$quality</span>";
|
||||
}
|
||||
|
||||
$diff = time() - $release->getUploadDate();
|
||||
|
||||
$date = getTimeDiff($diff);
|
||||
|
||||
$url = $release->getInfoLink();
|
||||
$urlName = getURLName($url);
|
||||
echo "<tr class=\"".($release->isBest() ? "info" : ($release->isTrusted() ? "success" : ""))."\"><td>".$release->getNumber()."</td><td>$group</td><td>$source</td><td>$quality</td><td>".$ep["titles"]["main"]["title"]."<br/><code>".$release->getOriginalTitle()."</code> <a href=\"release.php?release_id=".$release->getId()."&episode=".$release->getNumber()."\"><span class=\"glyphicon glyphicon-info-sign\"></span></a></td><td><a href=\"".$url."\" target=\"_blank\">".$urlName."</a></td><td>$date</td><td>".$ep["airs"]."</td></tr>";
|
||||
$lastN = $release->getNumber();
|
||||
}
|
||||
|
||||
for($n = $lastN + 1; $n <= (int) $xml->episodecount; ++$n){
|
||||
$ep = getEpisode($xml, $n);
|
||||
if($ep === null){
|
||||
echo "<tr><td>$n</td><td></td><td></td><td></td><td>UNAIRED</td><td></td><td></td><td>-</td></tr>";
|
||||
}else{
|
||||
echo "<tr><td>$n</td><td></td><td></td><td></td><td>UNAIRED - ".$ep["titles"]["main"]["title"]."</td><td></td><td></td><td>".$ep["airs"]."</td></tr>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
|
||||
printFooter();
|
54
web/titles.php
Normal file
54
web/titles.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
use Animarr\Release\Release;
|
||||
use Animarr\Torrent\Torrent;
|
||||
|
||||
$aid = 0;
|
||||
|
||||
if(isset($_GET["aid"])){
|
||||
$aid = intval($_GET["aid"]);
|
||||
}
|
||||
|
||||
$info = $aniDB->getAnime($aid);
|
||||
if($info === null){
|
||||
header("Location: index.php");
|
||||
die();
|
||||
}
|
||||
|
||||
if(isset($_POST) and isset($_POST["title"])){
|
||||
$database->addAlternateTitle($aid, $_POST["language"], $_POST["title"], $_POST["type"]);
|
||||
}
|
||||
|
||||
printHeader($info["title"]);
|
||||
|
||||
|
||||
echo "<h1>".$info["title"]." <a href=\"https://anidb.net/perl-bin/animedb.pl?show=anime&aid=$aid\" target=\"_blank\">[anidb-$aid]</a></h1>";
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>Title</th><th>Language</th><th>Type</th></tr>";
|
||||
|
||||
$types = [
|
||||
1 => "Main",
|
||||
2 => "Synonym",
|
||||
3 => "Short",
|
||||
4 => "Official",
|
||||
0 => "(Special)"
|
||||
];
|
||||
$typeSelect = "";
|
||||
foreach($types as $i => $n){
|
||||
$typeSelect .= "<option value=\"$i\">$n</option>";
|
||||
}
|
||||
foreach ($database->getAlternateTitles($aid) as $t) {
|
||||
echo "<tr><td>".htmlentities($t["title"])."</td><td>".htmlentities($t["language"])."</td><td>".$types[$t["type"]]."</td></tr>";
|
||||
}
|
||||
echo "<form action=\"titles.php?aid=$aid\" method=\"post\">";
|
||||
echo "<tr><td><input type=\"text\" name=\"title\"/></td><td><select name=\"language\"><option>x-jat</option><option>en</option></select></td><td><select name=\"type\">$typeSelect</td></tr>";
|
||||
echo "<tr><td colspan=3><input type=\"submit\" value=\"add new title\"\></td></tr>";
|
||||
echo "</form>";
|
||||
echo "</table>";
|
||||
|
||||
|
||||
printFooter();
|
111
web/top.php
Normal file
111
web/top.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
include "common.php";
|
||||
|
||||
$mal = \Animarr\Request::getURL("https://myanimelist.net/topanime.php?type=airing");
|
||||
$adb = \Animarr\Request::getURL("https://anidb.net/perl-bin/animedb.pl?show=animelist&type.tvseries=1&orderby.rating=0.2&orderby.name=1.1&noalias=1&h=1&airing=1");
|
||||
|
||||
$top = [];
|
||||
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
@$dom->loadHTML($mal);
|
||||
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$entries = $xpath->query("//tr[contains(concat(' ', normalize-space(@class), ' '), ' ranking-list ')]");
|
||||
foreach($entries as $entry){
|
||||
$name = trim((string) $entry->getElementsByTagName("td")->item(1)->getElementsByTagName("div")->item(0)->getElementsByTagName("div")->item(2)->getElementsByTagName("a")->item(0)->textContent);
|
||||
$score = (float) trim((string) $entry->getElementsByTagName("td")->item(2)->textContent);
|
||||
|
||||
$match = $aniDB->matchTitle($name);
|
||||
if($match === null){
|
||||
$link = "add.php?showName=". urlencode($name);
|
||||
$id = $name;
|
||||
}else{
|
||||
$name = $match["title"];
|
||||
$link = "add.php?aid=". $match["aid"];
|
||||
|
||||
if($database->isTracking($match["aid"])){
|
||||
$link = "";
|
||||
}
|
||||
$id = $match["aid"];
|
||||
}
|
||||
|
||||
if(isset($top[$id])){
|
||||
$top[$id]["scores"][] = $score;
|
||||
}else{
|
||||
$top[$id] = [
|
||||
"name" => $name,
|
||||
"link" => $link,
|
||||
"scores" => [$score],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
@$dom->loadHTML($adb);
|
||||
|
||||
|
||||
foreach($dom->getElementById("animelist")->getElementsByTagName("tr") as $entry){
|
||||
if($entry === null or $entry->getAttribute("class") == "header"){
|
||||
continue;
|
||||
}
|
||||
$name = trim((string) $entry->getElementsByTagName("td")->item(2)->getElementsByTagName("a")->item(0)->textContent);
|
||||
$score = trim((string) $entry->getElementsByTagName("td")->item(6)->textContent);
|
||||
$score = substr($score, 0, strpos($score, " ("));
|
||||
|
||||
$match = $aniDB->matchTitle($name);
|
||||
if($match === null){
|
||||
$link = "add.php?showName=". urlencode($name);
|
||||
$id = $name;
|
||||
}else{
|
||||
$name = $match["title"];
|
||||
$link = "add.php?aid=". $match["aid"];
|
||||
|
||||
if($database->isTracking($match["aid"])){
|
||||
$link = "";
|
||||
}
|
||||
$id = $match["aid"];
|
||||
}
|
||||
|
||||
if(isset($top[$id])){
|
||||
$top[$id]["scores"][] = $score;
|
||||
}else{
|
||||
$top[$id] = [
|
||||
"name" => $name,
|
||||
"link" => $link,
|
||||
"scores" => [$score],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
uasort($top, function($a, $b){
|
||||
$scorea = array_sum($a["scores"]) / count($a["scores"]);
|
||||
$scoreb = array_sum($b["scores"]) / count($b["scores"]);
|
||||
|
||||
if($scorea === $scoreb){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($scorea > $scoreb) ? -1 : 1;
|
||||
});
|
||||
|
||||
printHeader("Top Airing");
|
||||
|
||||
echo "<h1>Top Airing</h1>";
|
||||
|
||||
echo "<table class=\"table table-striped table-hover\">";
|
||||
|
||||
|
||||
echo "<tr><th>Show Name</th><th>Score</th><th>Actions</th></tr>";
|
||||
|
||||
foreach($top as $id => $data){
|
||||
$action = $data["link"] != "" ? "<a href=\"".$data["link"]."\"><span class=\"glyphicon glyphicon-plus\"></span></a>" : "<i>Tracked</i>";
|
||||
echo "<tr><td>".$data["name"]."</td><td>".round(array_sum($data["scores"]) / count($data["scores"]), 2)."</td><td>$action</td></tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
printFooter();
|
Loading…
Reference in a new issue