Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
vendor_perl/**
|
||||
103
.perlcriticrc
Normal file
103
.perlcriticrc
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env perlcritic
|
||||
severity = 5
|
||||
verbose = 8
|
||||
|
||||
# minorify some of warnings that are not really critical
|
||||
[-InputOutput::RequireCheckedClose]
|
||||
# What can we handle in case of error?
|
||||
|
||||
[-Modules::ProhibitExcessMainComplexity]
|
||||
# Noone cares
|
||||
|
||||
[-Subroutines::ProhibitExcessComplexity]
|
||||
# Noone cares
|
||||
|
||||
[-Subroutines::ProhibitExplicitReturnUndef]
|
||||
# But it's very handy sometimes!
|
||||
|
||||
[-ValuesAndExpressions::ProhibitMagicNumbers]
|
||||
# this policy makes too much false positives
|
||||
|
||||
[-RegularExpressions::RequireLineBoundaryMatching]
|
||||
# We use /m as needed
|
||||
|
||||
[-RegularExpressions::RequireExtendedFormatting]
|
||||
# We do not use extended formatting of regex, they still not THAT complex
|
||||
|
||||
[-RegularExpressions::RequireDotMatchAnything]
|
||||
# huh?
|
||||
|
||||
[-RegularExpressions::ProhibitEscapedMetacharacters]
|
||||
# Looks like it pointless
|
||||
|
||||
[-ControlStructures::ProhibitCascadingIfElse]
|
||||
# I like long if..else chains
|
||||
|
||||
[-ControlStructures::ProhibitPostfixControls]
|
||||
# In short lines we can afford ourselves to use postfix controls
|
||||
# but of course we should avoid such constructions in long lines.
|
||||
|
||||
[-Subroutines::ProhibitSubroutinePrototypes]
|
||||
# Contrary to Perl::Critic we consider prototypes as useful, even if
|
||||
# the compile-time checks of Perl are not perfect, Perl may give some
|
||||
# hints.
|
||||
|
||||
[-CodeLayout::RequireTidyCode]
|
||||
# Impossible to complete, because of on higher linting levels "## no critic..."
|
||||
# require be per-case on same line where critique is, but tidy align them
|
||||
# one line down.
|
||||
|
||||
[-CodeLayout::ProhibitParensWithBuiltins]
|
||||
# duh...
|
||||
|
||||
[-ValuesAndExpressions::ProhibitEmptyQuotes]
|
||||
# cause we consider following correct and useful: my $var = '';
|
||||
# the recommended use of q{} looks too complex: my $var = q{};
|
||||
|
||||
[-ValuesAndExpressions::ProhibitNoisyQuotes]
|
||||
# Other people, other opinions: "," is better readable than qw(,)
|
||||
|
||||
[-BuiltinFunctions::ProhibitSleepViaSelect]
|
||||
# We do not wish to use too much external modules.
|
||||
# select undef, undef, undef, 0.25; # actually is okay if you add comment that
|
||||
# it is sleep for 0.25 seconds
|
||||
|
||||
[-NamingConventions::Capitalization]
|
||||
# duh...
|
||||
# Noone cares, really
|
||||
|
||||
[-ControlStructures::ProhibitUnlessBlocks]
|
||||
# They should not be too complex - only one condition should be checked,
|
||||
# that's all
|
||||
|
||||
[-Miscellanea::ProhibitTies]
|
||||
# Just allow it. It's very handy stuff and extremely simple and safe enough to use.
|
||||
|
||||
[-ValuesAndExpressions::RequireNumberSeparators]
|
||||
# Not really
|
||||
|
||||
[-InputOutput::RequireBracedFileHandleWithPrint]
|
||||
# We prefer not to use braced file handles
|
||||
|
||||
[-Documentation::PodSpelling]
|
||||
# It doesn't help, but emits annoying errors
|
||||
|
||||
[Documentation::RequirePodSections]
|
||||
# Considering this as "soft" requirement. For modules that is (or will be)
|
||||
# stand-alone libs well formed documentation is highly desired.
|
||||
lib_sections = NAME | VERSION | DESCRIPTION | AUTHOR | LICENSE AND COPYRIGHT
|
||||
|
||||
[-ErrorHandling::RequireCheckingReturnValueOfEval]
|
||||
# In this app eval is not critical to error, so there is no need to check for it
|
||||
|
||||
[-InputOutput::RequireBriefOpen]
|
||||
# Yes, i do!
|
||||
|
||||
[-InputOutput::RequireCheckedSyscalls]
|
||||
# What can go wrong in "say"? what and how should i handle in this case?
|
||||
|
||||
[-ControlStructures::ProhibitDeepNests]
|
||||
# Oh, really? Maybe you know better solution?
|
||||
|
||||
[-Variables::ProhibitPunctuationVars]
|
||||
# I don't want use English here
|
||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2022 Sergei Fedosoff <eleksir@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
50
README.md
Normal file
50
README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Joyproxy
|
||||
|
||||
Эта версия приложения устаревшая, она не поддерживается и размещена
|
||||
исключительно в ознакомительных целях.
|
||||
|
||||
## Что это?
|
||||
|
||||
Своего рода API, делающий запросы в img#.reactor.cc
|
||||
|
||||
## Зачем это нужно?
|
||||
|
||||
Сайт reactor.cc не даёт возможности постить на внешние ресурсы ссылки без
|
||||
указания правильного referer-а.
|
||||
|
||||
Эта прокся призвана частично решить эту проблему через создание линков вида
|
||||
`https://сайт/joyproxy/img1.reactor.cc/pics/post/mp4/видосик.mp4`
|
||||
|
||||
Сгенерировать ссылку можно запостив урл с видосиком в форму на страничке
|
||||
`https://сайт/joyurl`
|
||||
|
||||
## Как это работает?
|
||||
|
||||
При обращении к сгенерённому урлу, апи вначале идёт на сайт, выкачивает
|
||||
видосик во временный каталог, отдаёт видосик клиенту. Есть встроенное
|
||||
ограничение на размер файла в 10Мб.
|
||||
|
||||
N.B. Файлы из временного каталога автоматически не вычищаются. Получается
|
||||
своего рода "перманентный" кэш.
|
||||
|
||||
## Как это запустить?
|
||||
|
||||
Я это запускаю как [PSGI][1]-приложение, через [UWSGI][2].
|
||||
|
||||
Для запуска понадобятся:
|
||||
|
||||
* uwsgi версии 2.0.15 или новее
|
||||
* perl-5.18 или новее (вплоть до 5.32.x)
|
||||
* модули perl-HTTP-Tiny, perl-URI-URL, perl-URL-Escape, perl-local-lib.
|
||||
|
||||
Можно использовать модули из системы, а можно подтянуть их из CPAN-а с помощью
|
||||
[cpanm][3] (но, правда, perl-local-lib всё же придётся поставить, так как без
|
||||
него ничего не заработает).
|
||||
|
||||
## Кому сказать спасибо?
|
||||
|
||||
Авторам, редакторам и тем, кто поддерживает `reactor.cc`
|
||||
|
||||
[1]: https://uwsgi-docs.readthedocs.io/en/latest/Perl.html
|
||||
[2]: https://github.com/unbit/uwsgi
|
||||
[3]: https://github.com/miyagawa/cpanminus
|
||||
5
bootstrap.sh
Normal file
5
bootstrap.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
cpanm -l vendor_perl --self-contained --force --update --installdeps .
|
||||
|
||||
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||
4
cpanfile
Normal file
4
cpanfile
Normal file
@@ -0,0 +1,4 @@
|
||||
requires 'HTTP::Tiny';
|
||||
requires 'URI::URL';
|
||||
requires 'URI::Escape';
|
||||
requires 'Plack::Util';
|
||||
12
joyproxy.ini
Normal file
12
joyproxy.ini
Normal file
@@ -0,0 +1,12 @@
|
||||
[settings]
|
||||
; This server can be behind reverse-proxy, set here http or https proto, correct for incomming connecions
|
||||
proto = http
|
||||
|
||||
; we listen on *:this_port
|
||||
port = 9980
|
||||
|
||||
; timeout for upstream connection
|
||||
timeout = 30
|
||||
|
||||
; loglevel - info or debug, info is what you want
|
||||
loglevel = debug
|
||||
193
joyproxy.pm
Normal file
193
joyproxy.pm
Normal file
@@ -0,0 +1,193 @@
|
||||
package joyproxy;
|
||||
|
||||
use 5.018;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
use open qw (:std :utf8);
|
||||
|
||||
use Carp qw (carp);
|
||||
use Fcntl;
|
||||
use HTTP::Tiny;
|
||||
use URI::URL;
|
||||
use URI::Escape qw (uri_unescape);
|
||||
|
||||
use Exporter qw (import);
|
||||
use version; our $VERSION = qw (1.1.1);
|
||||
our @EXPORT_OK = qw (joyproxy joyurl);
|
||||
|
||||
sub joyproxy ($) {
|
||||
my $str = shift;
|
||||
chomp $str;
|
||||
|
||||
# formal url check
|
||||
if ($str !~ /^img\d+\.reactor\.cc/xmsg) {
|
||||
return ('500', 'text/plain', 'This is not reactor video');
|
||||
}
|
||||
|
||||
my ($file, $filesize) = __dlfunc ("http://$str");
|
||||
|
||||
if (defined $file) {
|
||||
my $buf = '';
|
||||
|
||||
if (open my $VID, '<', $file) {
|
||||
binmode $VID;
|
||||
my $readlen = read ($VID, $buf, $filesize);
|
||||
close $VID;
|
||||
|
||||
if (defined $readlen) {
|
||||
if ($readlen != $filesize) {
|
||||
carp "Unable to read $file readlen does not match filesize: $readlen vs $filesize";
|
||||
return ('500', 'text/plain', "Unable to read file in temporary location\n");
|
||||
}
|
||||
} else {
|
||||
carp "Unable to read $file: $!";
|
||||
return ('500', 'text/plain', "Unable to read file in temporary location: $!\n");
|
||||
}
|
||||
|
||||
if ($file =~ /\.mp4$/xmsgi) {
|
||||
return ('200', 'video/mp4', $buf);
|
||||
} elsif ($file =~ /\.webm$/xmsgi) {
|
||||
return ('200', 'video/webm', $buf);
|
||||
} else {
|
||||
return ('200', 'video/mpeg', $buf);
|
||||
}
|
||||
} else {
|
||||
if (-f $file) {
|
||||
unlink $file;
|
||||
}
|
||||
|
||||
return ('500', 'text/plain', "Unable to open file in temporary location: $!\n");
|
||||
}
|
||||
} else {
|
||||
return ('500', 'text/plain', "Unable to get file from remote source!\n");
|
||||
}
|
||||
}
|
||||
|
||||
sub joyurl (@) {
|
||||
my $str = shift;
|
||||
my $host = shift;
|
||||
|
||||
# TODO: handle error here
|
||||
$str = uri_unescape $str;
|
||||
|
||||
if (length ($str) < 60) {
|
||||
$str = '';
|
||||
}
|
||||
|
||||
if ($str =~ /^joyurl=https?\:\/\/(img\d+\.reactor\.cc.+)/xmsg) {
|
||||
my $string = $1;
|
||||
# img1.reactor.cc/pics/post/webm/видосик.webm
|
||||
my @url = split /\//msx, $string;
|
||||
|
||||
if (($url[3] eq 'webm' || $url[3] eq 'mp4') &&
|
||||
($url[4] =~ /\.webm$/xmsg || $url[4] =~ /\.mp4$/xmsg)) {
|
||||
# we prefer mp4, right?, so
|
||||
my $fname;
|
||||
$fname = substr ($url[4], 0, -5) if (substr ($url[4], -5, 6) eq '.webm');
|
||||
$fname = substr ($url[4], 0, -4) if (substr ($url[4], -4, 4) eq '.mp4');
|
||||
|
||||
$string = sprintf (
|
||||
'https://%s/joyproxy/%s/%s/%s/mp4/%s.mp4',
|
||||
$host,
|
||||
$url[0],
|
||||
$url[1],
|
||||
$url[2],
|
||||
$fname
|
||||
);
|
||||
|
||||
$str = __urlencode ($string);
|
||||
} else {
|
||||
$str = '';
|
||||
}
|
||||
} else {
|
||||
$str = '';
|
||||
}
|
||||
|
||||
my $msg = << "EOL";
|
||||
<html>
|
||||
<body>
|
||||
<form method='get' action='joyurl'>
|
||||
<input type='text' name='joyurl' size=100 autofocus><br />
|
||||
<input type='submit' value='Post it!' style='font-size:115%;' />
|
||||
<br>$str
|
||||
</body>
|
||||
</html>\n
|
||||
EOL
|
||||
|
||||
return ('200', 'text/html', $msg);
|
||||
}
|
||||
|
||||
sub __urlencode($) {
|
||||
my $str = shift;
|
||||
my $urlobj = url $str;
|
||||
$str = $urlobj->as_string;
|
||||
$urlobj = '';
|
||||
return $str;
|
||||
}
|
||||
|
||||
sub __dlfunc($) {
|
||||
my $url = shift;
|
||||
|
||||
unless (-d '/tmp/joyproxy') {
|
||||
carp 'no /tmp/joyproxy';
|
||||
|
||||
unless (mkdir '/tmp/joyproxy') {
|
||||
return (undef, undef);
|
||||
}
|
||||
}
|
||||
|
||||
my @explodedurl = split /\//msx, $url;
|
||||
my $file = $explodedurl[$#explodedurl]; ## no critic (Variables::RequireNegativeIndices)
|
||||
$#explodedurl = -1; undef @explodedurl;
|
||||
$file = '/tmp/joyproxy/' . $file;
|
||||
$url = __urlencode ($url);
|
||||
|
||||
my $http = HTTP::Tiny->new (max_size => 10485760);
|
||||
|
||||
# use ->get because of ->mirror does not respect max_size
|
||||
my $response = $http->get (
|
||||
$url,
|
||||
{
|
||||
headers => {
|
||||
'Accept' => '*/*',
|
||||
'Accept-Encoding' => 'identity;q=1, *;q=0',
|
||||
'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||
'Range' => 'bytes=0-',
|
||||
'Referer' => 'http://old.reactor.cc/all',
|
||||
'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$http = '';
|
||||
|
||||
if ($response->{success}) {
|
||||
my $filesize = 0;
|
||||
|
||||
sysopen (my $FILE, $file, O_CREAT|O_TRUNC|O_WRONLY) or do {
|
||||
$response = '';
|
||||
return (undef, undef);
|
||||
};
|
||||
|
||||
binmode $FILE;
|
||||
|
||||
syswrite ($FILE, $response->{content}) or do {
|
||||
close $FILE;
|
||||
$response = '';
|
||||
return (undef, undef);
|
||||
};
|
||||
|
||||
close $FILE;
|
||||
$response = '';
|
||||
$filesize = (stat $file )[7];
|
||||
return ($file, $filesize);
|
||||
} else {
|
||||
$response = '';
|
||||
return (undef, undef);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||
48
joyproxy.psgi
Normal file
48
joyproxy.psgi
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use 5.018;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
use open qw (:std :utf8);
|
||||
use version; our $VERSION = qw (1.1.0);
|
||||
|
||||
use lib qw (. ./vendor_perl/lib/perl5);
|
||||
use joyproxy qw (joyurl joyproxy);
|
||||
|
||||
my $app = sub {
|
||||
my $env = shift;
|
||||
|
||||
my $msg = "Your Opinion is very important for us, please stand by.\n";
|
||||
my $status = '404';
|
||||
my $content = 'text/plain';
|
||||
|
||||
if ($env->{PATH_INFO} =~ /^\/joyproxy\/(.+)/xmsg) {
|
||||
my $joyproxyurl = $1;
|
||||
($status, $msg) = ('400', "Bad Request?\n");
|
||||
|
||||
if (($joyproxyurl =~ /\.mp4$/xmsgi) or ($joyproxyurl =~ /\.webm$/xmsgi)) {
|
||||
($status, $content, $msg) = joyproxy $joyproxyurl ;
|
||||
}
|
||||
}
|
||||
|
||||
if ($env->{PATH_INFO} =~ /^\/joyurl/xmsg) {
|
||||
my $querystring = $env->{QUERY_STRING} // '';
|
||||
|
||||
($status, $content, $msg) = joyurl ($querystring, $env->{HTTP_HOST});
|
||||
}
|
||||
|
||||
use bytes;
|
||||
my $length = length $msg;
|
||||
no bytes;
|
||||
|
||||
return [
|
||||
$status,
|
||||
[ 'Content-Type' => $content, 'Content-Length' => $length ],
|
||||
[ $msg ],
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
__END__
|
||||
# vim: set ft=perl noet ai ts=4 sw=4 sts=4:
|
||||
17
joyproxy.yaml
Normal file
17
joyproxy.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
uwsgi:
|
||||
http-socket: 127.0.0.1:3081
|
||||
socket-protocol: http
|
||||
so-keepalive: 0
|
||||
logformat: %(var.REMOTE_ADDR) %(var.REQUEST_METHOD) %(var.REQUEST_URI) %(status)
|
||||
processes: 1
|
||||
need-app: true
|
||||
log-reopen: yes
|
||||
reload-mercy: 5
|
||||
buffer-size: 32768
|
||||
die-on-term: true
|
||||
pidfile: /run/joyproxy/joyproxy.pid
|
||||
logto: /var/log/joyproxy/joyproxy.log
|
||||
plugins: psgi
|
||||
chdir: /var/lib/joyproxy
|
||||
psgi: /var/lib/joyproxy/joyproxy.psgi
|
||||
perl-local-lib: /var/lib/joyproxy/vendor_perl
|
||||
Reference in New Issue
Block a user