Skip to content

Commit eff5441

Browse files
authored
Merge pull request #1500 from shikorism/develop
Release 2025.5.0
2 parents 3dd0ab3 + 0be2d9c commit eff5441

28 files changed

+20940
-13317
lines changed

.devcontainer/compose.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
services:
2+
web:
3+
env_file: !reset []
4+
environment:
5+
APP_DEBUG: true
6+
scheduler:
7+
env_file: !reset []
8+
front:
9+
env_file: !reset []

.devcontainer/devcontainer.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "tissue",
3+
"dockerComposeFile": [
4+
"../compose.yaml",
5+
"compose.yaml"
6+
],
7+
"service": "web",
8+
"workspaceFolder": "/var/www/html",
9+
"customizations": {
10+
"vscode": {
11+
"extensions": [
12+
"EditorConfig.EditorConfig",
13+
"bmewburn.vscode-intelephense-client",
14+
"xdebug.php-debug"
15+
]
16+
}
17+
},
18+
"postCreateCommand": "node /var/www/html/.devcontainer/postCreateCommand.mjs"
19+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { spawn } from 'node:child_process';
2+
import fs from 'node:fs';
3+
4+
const envPath = '.env';
5+
const envExamplePath = '.env.example';
6+
7+
const spawnWithLog = async (command, args, options) => {
8+
if (!options.silent) {
9+
console.log(`Executing: ${command} ${args.join(' ')}`);
10+
}
11+
return new Promise((resolve, reject) => {
12+
let output = '';
13+
const childProcess = spawn(command, args, {
14+
...options,
15+
env: {
16+
...process.env,
17+
FORCE_COLOR: process.env.FORCE_COLOR || '1',
18+
},
19+
stdio: ['inherit', 'pipe', 'pipe']
20+
});
21+
22+
childProcess.stdout.on('data', (data) => {
23+
const str = data.toString();
24+
if (!options.silent) {
25+
process.stdout.write(str);
26+
}
27+
output += str;
28+
});
29+
30+
if (!options.silent) {
31+
childProcess.stderr.on('data', (data) => {
32+
const str = data.toString();
33+
process.stderr.write(str);
34+
});
35+
}
36+
37+
childProcess.on('close', (code) => {
38+
if (code === 0) {
39+
resolve(output);
40+
} else {
41+
reject(new Error(`Process exited with code ${code}`));
42+
}
43+
});
44+
});
45+
};
46+
47+
const waitDb = async () => {
48+
console.log('Waiting for database...');
49+
while (true) {
50+
try {
51+
const db = await spawnWithLog('php', ['artisan', 'db:monitor'], { shell: true, encoding: 'utf8', silent: true });
52+
if (db.includes('OK')) {
53+
break;
54+
}
55+
} catch (e) {
56+
}
57+
await new Promise(resolve => setTimeout(resolve, 1000));
58+
}
59+
};
60+
61+
let envExists = true;
62+
if (!fs.existsSync(envPath)) {
63+
envExists = false;
64+
fs.copyFileSync(envExamplePath, envPath);
65+
console.log('Created .env file from .env.example');
66+
}
67+
68+
await spawnWithLog('composer', ['install'], { shell: true, encoding: 'utf8' });
69+
70+
if (fs.readFileSync(envPath, 'utf8').match(/^APP_KEY=$/m)) {
71+
await spawnWithLog('php', ['artisan', 'key:generate', '-n'], { shell: true, encoding: 'utf8' });
72+
}
73+
74+
await waitDb();
75+
await spawnWithLog('php', ['artisan', 'migrate', '-n'], { shell: true, encoding: 'utf8' });
76+
await spawnWithLog('php', ['artisan', 'db:seed', '-n'], { shell: true, encoding: 'utf8' });
77+
78+
if (!envExists) {
79+
// .envがないのにDB上にはある場合、強制的に再生成した方が恐らく楽
80+
const passportOutput = await spawnWithLog('php', ['artisan', 'passport:install', '-n', '--force', '--no-ansi'], { shell: true, encoding: 'utf8' });
81+
82+
const personalAccessMatch = passportOutput.match(/Personal access client.*?Client ID.*?(\d+).*?Client secret.*?([A-Za-z0-9]+)/s);
83+
const passwordGrantMatch = passportOutput.match(/Password grant client.*?Client ID.*?(\d+).*?Client secret.*?([A-Za-z0-9]+)/s);
84+
if (personalAccessMatch && passwordGrantMatch) {
85+
const personalAccessClientId = personalAccessMatch[1];
86+
const personalAccessClientSecret = personalAccessMatch[2];
87+
88+
const envContent = fs.readFileSync(envPath, 'utf8');
89+
const newEnvContent = envContent +
90+
`\nPASSPORT_PERSONAL_ACCESS_CLIENT_ID=${personalAccessClientId}` +
91+
`\nPASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=${personalAccessClientSecret}`;
92+
93+
fs.writeFileSync(envPath, newEnvContent);
94+
console.log('Added Passport configuration to .env file');
95+
} else {
96+
throw new Error('Failed to install Passport');
97+
}
98+
}
99+
100+
await spawnWithLog('chown', ['-R', 'www-data', '/var/www/html/storage'], { shell: true, encoding: 'utf8' });
101+
102+
console.log('Setup completed successfully!');
103+
console.log("You can access the application at http://localhost:4545 .");

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
/storage/*.key
1212
/vendor
1313
/.idea
14-
/.vscode
14+
/.vscode/*
15+
!/.vscode/launch.json
1516
/.vagrant
1617
Homestead.json
1718
Homestead.yaml

.vscode/launch.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Listen for Xdebug",
6+
"type": "php",
7+
"request": "launch",
8+
"port": 9003,
9+
"pathMappings": {
10+
"/var/www/html": "${workspaceFolder}"
11+
}
12+
}
13+
]
14+
}

app/MetadataResolver/FanzaResolver.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,34 @@ public function resolve(string $url): Metadata
5656
$metadata->title = trim($crawler->filter('#title')->text(''));
5757
$metadata->description = trim(strip_tags(str_replace('【FANZA(ファンザ)】', '', $crawler->filter('meta[name="description"]')->attr('content'))));
5858
$metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $crawler->filter('meta[property="og:image"]')->attr('content'));
59-
$metadata->tags = $this->array_finish($crawler->filter('.box-rank+table a[href*="list/?"]')->extract(['_text']));
59+
60+
$tags = $crawler->filter('.box-rank+table a[href*="list/?"]')->extract(['_text']);
61+
62+
// 追加の出演者情報があれば取得
63+
$performerUrlPattern = '~/digital/(videoa|videoc|anime)/-/detail/ajax-performer/=/data=([^\'"]+)~';
64+
if (preg_match($performerUrlPattern, $html, $matches)) {
65+
$performerUrl = $matches[0];
66+
$performerRes = $this->client->get('https://www.dmm.co.jp' . $performerUrl);
67+
$performerHtml = (string) $performerRes->getBody();
68+
$performerCrawler = new Crawler($performerHtml);
69+
$performerTags = $this->array_finish($performerCrawler->filter('a')->extract(['_text']));
70+
$tags = array_merge($performerTags, $tags);
71+
}
72+
73+
$metadata->tags = $this->array_finish($tags);
6074

6175
return $metadata;
6276
}
6377

6478
// 同人
6579
if (mb_strpos($url, 'www.dmm.co.jp/dc/doujin/-/detail/') !== false) {
66-
$genre = $this->array_finish($crawler->filter('.m-productInformation a:not([href="#update-top"])')->extract(['_text']));
80+
$genre = $this->array_finish($crawler->filter('.m-productInformation .informationList a:not([href="#update-top"])')->extract(['_text']));
6781
$genre = array_filter($genre, (function ($text) {
6882
return !preg_match('~%OFF対象$~', $text);
6983
}));
7084

7185
$metadata = new Metadata();
72-
$metadata->title = $crawler->filter('meta[property="og:title"]')->attr('content');
86+
$metadata->title = trim($crawler->filter('meta[property="og:title"]')->attr('content'));
7387
$metadata->description = trim($crawler->filter('.summary__txt')->text('', false));
7488
$metadata->image = $crawler->filter('meta[property="og:image"]')->attr('content');
7589
$metadata->tags = array_merge($genre, [$crawler->filter('.circleName__txt')->text('')]);
@@ -99,10 +113,10 @@ public function resolve(string $url): Metadata
99113
// PCゲーム
100114
if (mb_strpos($url, 'dlsoft.dmm.co.jp/detail/') !== false) {
101115
$metadata = new Metadata();
102-
$metadata->title = trim($crawler->filter('#title')->text(''));
116+
$metadata->title = trim($crawler->filter('.productTitle__headline')->text(''));
103117
$metadata->description = trim($crawler->filter('.area-detail-read .text-overflow')->text('', false));
104118
$metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $crawler->filter('meta[property="og:image"]')->attr('content'));
105-
$metadata->tags = $this->array_finish($crawler->filter('.container02 table a[href*="list/article="]')->extract(['_text']));
119+
$metadata->tags = $this->array_finish($crawler->filter('.contentsDetailBottom a[href*="list/?"]')->extract(['_text']));
106120

107121
return $metadata;
108122
}

app/MetadataResolver/MelonbooksResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function resolve(string $url): Metadata
5050
}
5151

5252
// 抽出
53-
preg_match('~^(.+)\((.+)\)の通販・購入はメロンブックス~', $metadata->title, $match);
53+
preg_match('~^(.+)((.+)の通販・購入はメロンブックス~', $metadata->title, $match);
5454
$title = $match[1];
5555
$maker = $match[2];
5656

app/Utilities/Formatter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function normalizeUrl($url)
5050
$url = urldecode($url);
5151

5252
// Remove Hashbang
53-
$url = preg_replace('~/#!/~u', '/', $url);
53+
$url = str_replace('/#!/', '/', $url);
5454

5555
// Sort query parameters
5656
$parts = parse_url($url);

composer.lock

Lines changed: 12 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"@eslint/js": "^9.22.0",
1818
"@redocly/cli": "^1.19.0",
1919
"@tabler/icons-webfont": "^3.31.0",
20-
"@tanstack/react-query": "^4.0.0",
21-
"@tanstack/react-query-devtools": "^4.0.0",
20+
"@tanstack/react-query": "^5.70.0",
21+
"@tanstack/react-query-devtools": "^5.70.0",
2222
"@types/bootstrap": "^4.6.2",
2323
"@types/clipboard": "^2.0.10",
2424
"@types/jquery": "^3.5.30",
@@ -38,7 +38,7 @@
3838
"eslint": "^9.22.0",
3939
"eslint-config-prettier": "^10.1.1",
4040
"eslint-plugin-jquery": "^1.5.1",
41-
"eslint-plugin-prettier": "^5.2.3",
41+
"eslint-plugin-prettier": "^5.2.5",
4242
"eslint-plugin-react": "^7.37.4",
4343
"globals": "^15.9.0",
4444
"husky": "^9.1.7",
@@ -50,7 +50,7 @@
5050
"popper.js": "^1.14.7",
5151
"postcss": "^8.4.41",
5252
"postcss-scss": "^4.0.9",
53-
"prettier": "^3.3.3",
53+
"prettier": "^3.5.3",
5454
"qs": "^6.14.0",
5555
"react": "^18.3.1",
5656
"react-bootstrap": "^1.6.4",
@@ -62,7 +62,7 @@
6262
"stylelint-config-recess-order": "^3.1.0",
6363
"typescript": "^5.5.4",
6464
"typescript-eslint": "^8.27.0",
65-
"vite": "^6.2.2"
65+
"vite": "^6.2.3"
6666
},
6767
"stylelint": {
6868
"customSyntax": "postcss-scss",

0 commit comments

Comments
 (0)