Issues (578)

app/Services/ReleaseCreationService.php (2 issues)

Labels
1
<?php
2
3
namespace App\Services;
4
5
use App\Enums\CollectionFileCheckStatus;
6
use App\Models\Category;
7
use App\Models\Collection;
8
use App\Models\Predb;
9
use App\Models\Release;
10
use App\Models\ReleaseRegex;
11
use App\Models\ReleasesGroups;
12
use App\Models\UsenetGroup;
13
use App\Services\Categorization\CategorizationService;
14
use Blacklight\ColorCLI;
15
use Blacklight\NZB;
16
use Blacklight\ReleaseCleaning;
17
use Illuminate\Support\Facades\DB;
18
use Illuminate\Support\Str;
19
20
class ReleaseCreationService
21
{
22
    public function __construct(
23
        private readonly ColorCLI $colorCLI,
24
        private readonly ReleaseCleaning $releaseCleaning
25
    ) {}
26
27
    /**
28
     * Create releases from complete collections.
29
     *
30
     * @return array{added:int,dupes:int}
31
     * @throws \Throwable
32
     */
33
    public function createReleases(int|string|null $groupID, int $limit, bool $echoCLI): array
34
    {
35
        $startTime = now()->toImmutable();
36
        $categorize = new CategorizationService();
37
        $returnCount = 0;
38
        $duplicate = 0;
39
40
        if ($echoCLI) {
41
            $this->colorCLI->header('Process Releases -> Create releases from complete collections.');
42
        }
43
44
        $collectionsQuery = Collection::query()
45
            ->where('collections.filecheck', CollectionFileCheckStatus::Sized->value)
46
            ->where('collections.filesize', '>', 0);
47
        if (! empty($groupID)) {
48
            $collectionsQuery->where('collections.groups_id', $groupID);
49
        }
50
        $collectionsQuery->select(['collections.*', 'usenet_groups.name as gname'])
51
            ->join('usenet_groups', 'usenet_groups.id', '=', 'collections.groups_id')
52
            ->limit($limit);
53
        $collections = $collectionsQuery->get();
54
55
        if ($echoCLI && $collections->count() > 0) {
56
            $this->colorCLI->primary(\count($collections).' Collections ready to be converted to releases.', true);
57
        }
58
59
        foreach ($collections as $collection) {
60
            $cleanRelName = mb_convert_encoding(str_replace(['#', '@', '$', '%', '^', '§', '¨', '©', 'Ö'], '', $collection->subject), 'UTF-8', mb_list_encodings());
61
            $fromName = mb_convert_encoding(trim($collection->fromname, "'"), 'UTF-8', mb_list_encodings());
62
63
            // Deduplicate by name, from, and ~size
64
            $dupeCheck = Release::query()
65
                ->where(['name' => $cleanRelName, 'fromname' => $fromName])
66
                ->whereBetween('size', [$collection->filesize * .99, $collection->filesize * 1.01])
67
                ->first(['id']);
68
69
            if ($dupeCheck === null) {
70
                $cleanedMeta = $this->releaseCleaning->releaseCleaner(
71
                    $collection->subject,
72
                    $collection->fromname,
73
                    $collection->gname
0 ignored issues
show
The property gname does not seem to exist on App\Models\Collection. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
74
                );
75
76
                $namingRegexId = 0;
77
                if (\is_array($cleanedMeta)) {
78
                    $namingRegexId = isset($cleanedMeta['id']) ? (int) $cleanedMeta['id'] : 0;
79
                }
80
81
                if (\is_array($cleanedMeta)) {
82
                    $properName = $cleanedMeta['properlynamed'] ?? false;
83
                    $preID = $cleanedMeta['predb'] ?? false;
84
                    $cleanedName = $cleanedMeta['cleansubject'] ?? $cleanRelName;
85
                } else {
86
                    $properName = true;
87
                    $preID = false;
88
                    $cleanedName = $cleanRelName;
89
                }
90
91
                if ($preID === false && $cleanedName !== '') {
92
                    $preMatch = Predb::matchPre($cleanedName);
93
                    if ($preMatch !== false) {
94
                        $cleanedName = $preMatch['title'];
95
                        $preID = $preMatch['predb_id'];
96
                        $properName = true;
97
                    }
98
                }
99
100
                $determinedCategory = $categorize->determineCategory($collection->groups_id, $cleanedName);
101
102
                $searchName = ! empty($cleanedName) ? mb_convert_encoding($cleanedName, 'UTF-8', mb_list_encodings()) : $cleanRelName;
103
104
                $releaseID = Release::insertRelease([
105
                    'name' => $cleanRelName,
106
                    'searchname' => $searchName,
107
                    'totalpart' => $collection->totalfiles,
108
                    'groups_id' => $collection->groups_id,
109
                    'guid' => Str::uuid()->toString(),
110
                    'postdate' => $collection->date,
111
                    'fromname' => $fromName,
112
                    'size' => $collection->filesize,
113
                    'categories_id' => $determinedCategory['categories_id'] ?? Category::OTHER_MISC,
114
                    'isrenamed' => $properName === true ? 1 : 0,
115
                    'predb_id' => $preID === false ? 0 : $preID,
116
                    'nzbstatus' => NZB::NZB_NONE,
117
                    'ishashed' => preg_match('/^[a-fA-F0-9]{32}\b|^[a-fA-F0-9]{40}\b|^[a-fA-F0-9]{64}\b|^[a-fA-F0-9]{96}\b|^[a-fA-F0-9]{128}\b/i', $searchName) ? 1 : 0,
0 ignored issues
show
It seems like $searchName can also be of type array; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
                    'ishashed' => preg_match('/^[a-fA-F0-9]{32}\b|^[a-fA-F0-9]{40}\b|^[a-fA-F0-9]{64}\b|^[a-fA-F0-9]{96}\b|^[a-fA-F0-9]{128}\b/i', /** @scrutinizer ignore-type */ $searchName) ? 1 : 0,
Loading history...
118
                ]);
119
120
                if ($releaseID !== null) {
121
                    DB::transaction(static function () use ($collection, $releaseID) {
122
                        Collection::query()->where('id', $collection->id)->update([
123
                            'filecheck' => CollectionFileCheckStatus::Inserted->value,
124
                            'releases_id' => $releaseID,
125
                        ]);
126
                    }, 10);
127
128
                    ReleaseRegex::insertOrIgnore([
129
                        'releases_id' => $releaseID,
130
                        'collection_regex_id' => $collection->collection_regexes_id,
131
                        'naming_regex_id' => $namingRegexId,
132
                    ]);
133
134
                    if (preg_match_all('#(\S+):\S+#', $collection->xref, $hits)) {
135
                        foreach ($hits[1] as $grp) {
136
                            $grpTmp = UsenetGroup::isValidGroup($grp);
137
                            if ($grpTmp !== false) {
138
                                $xrefGrpID = UsenetGroup::getIDByName($grpTmp);
139
                                if ($xrefGrpID === '') {
140
                                    $xrefGrpID = UsenetGroup::addGroup([
141
                                        'name' => $grpTmp,
142
                                        'description' => 'Added by Release processing',
143
                                        'backfill_target' => 1,
144
                                        'first_record' => 0,
145
                                        'last_record' => 0,
146
                                        'active' => 0,
147
                                        'backfill' => 0,
148
                                        'minfilestoformrelease' => '',
149
                                        'minsizetoformrelease' => '',
150
                                    ]);
151
                                }
152
153
                                $relGroupsChk = ReleasesGroups::query()->where([
154
                                    ['releases_id', '=', $releaseID],
155
                                    ['groups_id', '=', $xrefGrpID],
156
                                ])->first();
157
158
                                if ($relGroupsChk === null) {
159
                                    ReleasesGroups::query()->insert([
160
                                        'releases_id' => $releaseID,
161
                                        'groups_id' => $xrefGrpID,
162
                                    ]);
163
                                }
164
                            }
165
                        }
166
                    }
167
168
                    $returnCount++;
169
                    if ($echoCLI) {
170
                        echo "Added $returnCount releases.\r";
171
                    }
172
                }
173
            } else {
174
                DB::transaction(static function () use ($collection) {
175
                    Collection::query()->where('collectionhash', $collection->collectionhash)->delete();
176
                }, 10);
177
178
                $duplicate++;
179
            }
180
        }
181
182
        $totalTime = now()->diffInSeconds($startTime, true);
183
        if ($echoCLI) {
184
            $this->colorCLI->primary(
185
                PHP_EOL.
186
                number_format($returnCount).
187
                ' Releases added and '.
188
                number_format($duplicate).
189
                ' duplicate collections deleted in '.
190
                $totalTime.\Illuminate\Support\Str::plural(' second', $totalTime),
191
                true
192
            );
193
        }
194
195
        return ['added' => $returnCount, 'dupes' => $duplicate];
196
    }
197
}
198