CDMClient 1.0.2__tar.gz → 1.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/PKG-INFO +1 -19
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/SOURCES.txt +0 -1
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/entry_points.txt +0 -1
- {cdmclient-1.0.2 → cdmclient-1.0.3}/PKG-INFO +1 -19
- {cdmclient-1.0.2 → cdmclient-1.0.3}/README.md +0 -18
- {cdmclient-1.0.2 → cdmclient-1.0.3}/pyproject.toml +1 -2
- cdmclient-1.0.2/cdm_client/migrate_torrents.py +0 -374
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/dependency_links.txt +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/requires.txt +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/CDMClient.egg-info/top_level.txt +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/LICENSE +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/__init__.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/cdm_client.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/config.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/database_adapter.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/qbittorrent_adapter.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/torrent_client_adapter_base.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/torrent_client_factory.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/cdm_client/transmission_adapter.py +0 -0
- {cdmclient-1.0.2 → cdmclient-1.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CDMClient
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: CDMClient
|
|
5
5
|
Author-email: Aron Radics <radics.aron.jozsef@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/radaron/CDMClient
|
|
@@ -100,21 +100,3 @@ To monitor the service logs, use the following command:
|
|
|
100
100
|
```shell
|
|
101
101
|
journalctl -fu cdm-client
|
|
102
102
|
```
|
|
103
|
-
|
|
104
|
-
## Torrent Migration
|
|
105
|
-
You can migrate torrents between Transmission and qBittorrent with:
|
|
106
|
-
```shell
|
|
107
|
-
cdm-migrate --source-type transmission --target-type qbittorrent \
|
|
108
|
-
--source-host 127.0.0.1 --source-port 9091 --source-username user --source-password pass \
|
|
109
|
-
--target-host 127.0.0.1 --target-port 8080 --target-username admin --target-password adminadmin
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Recommended first run (dry-run)
|
|
113
|
-
```shell
|
|
114
|
-
cdm-migrate --source-type transmission --target-type qbittorrent --dry-run
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Reverse direction (qBittorrent -> Transmission)
|
|
118
|
-
```shell
|
|
119
|
-
cdm-migrate --source-type qbittorrent --target-type transmission
|
|
120
|
-
```
|
|
@@ -11,7 +11,6 @@ cdm_client/__init__.py
|
|
|
11
11
|
cdm_client/cdm_client.py
|
|
12
12
|
cdm_client/config.py
|
|
13
13
|
cdm_client/database_adapter.py
|
|
14
|
-
cdm_client/migrate_torrents.py
|
|
15
14
|
cdm_client/qbittorrent_adapter.py
|
|
16
15
|
cdm_client/torrent_client_adapter_base.py
|
|
17
16
|
cdm_client/torrent_client_factory.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CDMClient
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: CDMClient
|
|
5
5
|
Author-email: Aron Radics <radics.aron.jozsef@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/radaron/CDMClient
|
|
@@ -100,21 +100,3 @@ To monitor the service logs, use the following command:
|
|
|
100
100
|
```shell
|
|
101
101
|
journalctl -fu cdm-client
|
|
102
102
|
```
|
|
103
|
-
|
|
104
|
-
## Torrent Migration
|
|
105
|
-
You can migrate torrents between Transmission and qBittorrent with:
|
|
106
|
-
```shell
|
|
107
|
-
cdm-migrate --source-type transmission --target-type qbittorrent \
|
|
108
|
-
--source-host 127.0.0.1 --source-port 9091 --source-username user --source-password pass \
|
|
109
|
-
--target-host 127.0.0.1 --target-port 8080 --target-username admin --target-password adminadmin
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Recommended first run (dry-run)
|
|
113
|
-
```shell
|
|
114
|
-
cdm-migrate --source-type transmission --target-type qbittorrent --dry-run
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Reverse direction (qBittorrent -> Transmission)
|
|
118
|
-
```shell
|
|
119
|
-
cdm-migrate --source-type qbittorrent --target-type transmission
|
|
120
|
-
```
|
|
@@ -81,21 +81,3 @@ To monitor the service logs, use the following command:
|
|
|
81
81
|
```shell
|
|
82
82
|
journalctl -fu cdm-client
|
|
83
83
|
```
|
|
84
|
-
|
|
85
|
-
## Torrent Migration
|
|
86
|
-
You can migrate torrents between Transmission and qBittorrent with:
|
|
87
|
-
```shell
|
|
88
|
-
cdm-migrate --source-type transmission --target-type qbittorrent \
|
|
89
|
-
--source-host 127.0.0.1 --source-port 9091 --source-username user --source-password pass \
|
|
90
|
-
--target-host 127.0.0.1 --target-port 8080 --target-username admin --target-password adminadmin
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Recommended first run (dry-run)
|
|
94
|
-
```shell
|
|
95
|
-
cdm-migrate --source-type transmission --target-type qbittorrent --dry-run
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Reverse direction (qBittorrent -> Transmission)
|
|
99
|
-
```shell
|
|
100
|
-
cdm-migrate --source-type qbittorrent --target-type transmission
|
|
101
|
-
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "CDMClient"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
4
4
|
description = "CDMClient"
|
|
5
5
|
authors = [{name = "Aron Radics", email = "radics.aron.jozsef@gmail.com"}]
|
|
6
6
|
requires-python = ">=3.9"
|
|
@@ -44,4 +44,3 @@ package = true
|
|
|
44
44
|
|
|
45
45
|
[project.scripts]
|
|
46
46
|
cdm-client = "cdm_client.cdm_client:main"
|
|
47
|
-
cdm-migrate = "cdm_client.migrate_torrents:main"
|
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import logging
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
from transmission_rpc import Torrent
|
|
7
|
-
|
|
8
|
-
from cdm_client.database_adapter import DatabaseAdapter
|
|
9
|
-
from cdm_client.qbittorrent_adapter import QBitTorrentAdapter
|
|
10
|
-
from cdm_client.torrent_client_adapter_base import TorrentClientAdapterBase
|
|
11
|
-
from cdm_client.torrent_client_factory import (
|
|
12
|
-
TorrentClientType,
|
|
13
|
-
create_torrent_client_adapter,
|
|
14
|
-
)
|
|
15
|
-
from cdm_client.transmission_adapter import TransmissionAdapter
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class TorrentMigrationItem:
|
|
20
|
-
source_id: int
|
|
21
|
-
hash_value: str
|
|
22
|
-
name: str
|
|
23
|
-
download_dir: str
|
|
24
|
-
magnet_link: Optional[str]
|
|
25
|
-
payload_bytes: Optional[bytes]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@dataclass
|
|
29
|
-
class MigrationStats:
|
|
30
|
-
migrated: int = 0
|
|
31
|
-
skipped_duplicate: int = 0
|
|
32
|
-
failed_add: int = 0
|
|
33
|
-
failed_lookup: int = 0
|
|
34
|
-
failed_db_update: int = 0
|
|
35
|
-
source_removed: int = 0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _hash_to_id(torrent_hash: str) -> int:
|
|
39
|
-
return int(torrent_hash[:8], 16)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _normalize_hash(torrent_hash: str) -> str:
|
|
43
|
-
return torrent_hash.lower()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _build_parser() -> argparse.ArgumentParser:
|
|
47
|
-
parser = argparse.ArgumentParser(
|
|
48
|
-
description="Migrate torrents between Transmission and qBittorrent."
|
|
49
|
-
)
|
|
50
|
-
parser.add_argument(
|
|
51
|
-
"--source-type",
|
|
52
|
-
required=True,
|
|
53
|
-
choices=[
|
|
54
|
-
TorrentClientType.TRANSMISSION.value,
|
|
55
|
-
TorrentClientType.QBITTORRENT.value,
|
|
56
|
-
],
|
|
57
|
-
)
|
|
58
|
-
parser.add_argument(
|
|
59
|
-
"--target-type",
|
|
60
|
-
required=True,
|
|
61
|
-
choices=[
|
|
62
|
-
TorrentClientType.TRANSMISSION.value,
|
|
63
|
-
TorrentClientType.QBITTORRENT.value,
|
|
64
|
-
],
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
parser.add_argument("--source-host")
|
|
68
|
-
parser.add_argument("--source-port", type=int)
|
|
69
|
-
parser.add_argument("--source-username")
|
|
70
|
-
parser.add_argument("--source-password")
|
|
71
|
-
|
|
72
|
-
parser.add_argument("--target-host")
|
|
73
|
-
parser.add_argument("--target-port", type=int)
|
|
74
|
-
parser.add_argument("--target-username")
|
|
75
|
-
parser.add_argument("--target-password")
|
|
76
|
-
|
|
77
|
-
parser.add_argument("--dry-run", action="store_true")
|
|
78
|
-
parser.add_argument("--verbose", action="store_true")
|
|
79
|
-
return parser
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _default_host() -> str:
|
|
83
|
-
return "127.0.0.1"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def _default_port(client_type: TorrentClientType) -> int:
|
|
87
|
-
if client_type == TorrentClientType.TRANSMISSION:
|
|
88
|
-
return 9091
|
|
89
|
-
return 8080
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _list_source_items(
|
|
93
|
-
source_type: TorrentClientType, source_adapter: TorrentClientAdapterBase
|
|
94
|
-
) -> list[TorrentMigrationItem]:
|
|
95
|
-
if source_type == TorrentClientType.TRANSMISSION:
|
|
96
|
-
if not isinstance(source_adapter, TransmissionAdapter):
|
|
97
|
-
raise TypeError("Invalid source adapter for Transmission")
|
|
98
|
-
torrents = source_adapter._client.get_torrents()
|
|
99
|
-
items: list[TorrentMigrationItem] = []
|
|
100
|
-
for torrent in torrents:
|
|
101
|
-
if not isinstance(torrent, Torrent):
|
|
102
|
-
continue
|
|
103
|
-
items.append(
|
|
104
|
-
TorrentMigrationItem(
|
|
105
|
-
source_id=int(torrent.id),
|
|
106
|
-
hash_value=_normalize_hash(torrent.hashString),
|
|
107
|
-
name=torrent.name,
|
|
108
|
-
download_dir=torrent.download_dir,
|
|
109
|
-
magnet_link=torrent.magnet_link,
|
|
110
|
-
payload_bytes=None,
|
|
111
|
-
)
|
|
112
|
-
)
|
|
113
|
-
return items
|
|
114
|
-
|
|
115
|
-
if not isinstance(source_adapter, QBitTorrentAdapter):
|
|
116
|
-
raise TypeError("Invalid source adapter for qBittorrent")
|
|
117
|
-
items = []
|
|
118
|
-
for torrent in source_adapter._client.torrents_info():
|
|
119
|
-
torrent_hash = _normalize_hash(torrent.hash)
|
|
120
|
-
payload_bytes: Optional[bytes] = None
|
|
121
|
-
try:
|
|
122
|
-
payload_bytes = source_adapter._client.torrents_export(
|
|
123
|
-
torrent_hash=torrent_hash
|
|
124
|
-
)
|
|
125
|
-
except Exception:
|
|
126
|
-
payload_bytes = None
|
|
127
|
-
magnet_link: Optional[str] = None
|
|
128
|
-
if hasattr(torrent, "magnet_uri"):
|
|
129
|
-
magnet_candidate = getattr(torrent, "magnet_uri")
|
|
130
|
-
if isinstance(magnet_candidate, str) and magnet_candidate:
|
|
131
|
-
magnet_link = magnet_candidate
|
|
132
|
-
items.append(
|
|
133
|
-
TorrentMigrationItem(
|
|
134
|
-
source_id=_hash_to_id(torrent_hash),
|
|
135
|
-
hash_value=torrent_hash,
|
|
136
|
-
name=torrent.name,
|
|
137
|
-
download_dir=torrent.save_path,
|
|
138
|
-
magnet_link=magnet_link,
|
|
139
|
-
payload_bytes=payload_bytes,
|
|
140
|
-
)
|
|
141
|
-
)
|
|
142
|
-
return items
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def _build_target_hash_index(
|
|
146
|
-
target_type: TorrentClientType, target_adapter: TorrentClientAdapterBase
|
|
147
|
-
) -> dict[str, int]:
|
|
148
|
-
hash_to_id: dict[str, int] = {}
|
|
149
|
-
if target_type == TorrentClientType.TRANSMISSION:
|
|
150
|
-
if not isinstance(target_adapter, TransmissionAdapter):
|
|
151
|
-
raise TypeError("Invalid target adapter for Transmission")
|
|
152
|
-
for torrent in target_adapter._client.get_torrents():
|
|
153
|
-
if not isinstance(torrent, Torrent):
|
|
154
|
-
continue
|
|
155
|
-
hash_to_id[_normalize_hash(torrent.hashString)] = int(torrent.id)
|
|
156
|
-
return hash_to_id
|
|
157
|
-
|
|
158
|
-
if not isinstance(target_adapter, QBitTorrentAdapter):
|
|
159
|
-
raise TypeError("Invalid target adapter for qBittorrent")
|
|
160
|
-
for torrent in target_adapter._client.torrents_info():
|
|
161
|
-
torrent_hash = _normalize_hash(torrent.hash)
|
|
162
|
-
hash_to_id[torrent_hash] = _hash_to_id(torrent_hash)
|
|
163
|
-
return hash_to_id
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def _add_to_target(
|
|
167
|
-
item: TorrentMigrationItem,
|
|
168
|
-
target_type: TorrentClientType,
|
|
169
|
-
target_adapter: TorrentClientAdapterBase,
|
|
170
|
-
) -> Optional[int]:
|
|
171
|
-
if target_type == TorrentClientType.TRANSMISSION:
|
|
172
|
-
if not isinstance(target_adapter, TransmissionAdapter):
|
|
173
|
-
raise TypeError("Invalid target adapter for Transmission")
|
|
174
|
-
if item.payload_bytes:
|
|
175
|
-
created = target_adapter.add_torrent(item.payload_bytes, item.download_dir)
|
|
176
|
-
if created is None:
|
|
177
|
-
return None
|
|
178
|
-
return int(created.id)
|
|
179
|
-
if item.magnet_link:
|
|
180
|
-
created = target_adapter._client.add_torrent(
|
|
181
|
-
item.magnet_link, download_dir=item.download_dir
|
|
182
|
-
)
|
|
183
|
-
return int(created.id)
|
|
184
|
-
return None
|
|
185
|
-
|
|
186
|
-
if not isinstance(target_adapter, QBitTorrentAdapter):
|
|
187
|
-
raise TypeError("Invalid target adapter for qBittorrent")
|
|
188
|
-
if item.payload_bytes:
|
|
189
|
-
added = target_adapter.add_torrent(item.payload_bytes, item.download_dir)
|
|
190
|
-
if added is None:
|
|
191
|
-
return None
|
|
192
|
-
elif item.magnet_link:
|
|
193
|
-
target_adapter._client.torrents_add(
|
|
194
|
-
urls=item.magnet_link, save_path=item.download_dir
|
|
195
|
-
)
|
|
196
|
-
else:
|
|
197
|
-
return None
|
|
198
|
-
for torrent in target_adapter._client.torrents_info():
|
|
199
|
-
if _normalize_hash(torrent.hash) == item.hash_value:
|
|
200
|
-
return _hash_to_id(item.hash_value)
|
|
201
|
-
return None
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def _reconcile_mapping(source_id: int, target_id: int) -> bool:
|
|
205
|
-
with DatabaseAdapter() as db_adapter:
|
|
206
|
-
tracker_id = db_adapter.get_tracker_id_by_torrent_id(source_id)
|
|
207
|
-
logging.debug(
|
|
208
|
-
"source_id=%s, target_id=%s, tracker_id=%s",
|
|
209
|
-
source_id,
|
|
210
|
-
target_id,
|
|
211
|
-
tracker_id,
|
|
212
|
-
)
|
|
213
|
-
if tracker_id is None:
|
|
214
|
-
return True
|
|
215
|
-
return db_adapter.update_torrent_id(tracker_id, target_id)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def _parse_types_or_exit(
|
|
219
|
-
source_type_raw: str,
|
|
220
|
-
target_type_raw: str,
|
|
221
|
-
) -> tuple[TorrentClientType, TorrentClientType]:
|
|
222
|
-
try:
|
|
223
|
-
source_type = TorrentClientType(source_type_raw)
|
|
224
|
-
target_type = TorrentClientType(target_type_raw)
|
|
225
|
-
except ValueError as exc:
|
|
226
|
-
raise SystemExit(2) from exc
|
|
227
|
-
return source_type, target_type
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def _validate_args_or_exit(
|
|
231
|
-
args: argparse.Namespace,
|
|
232
|
-
source_type: TorrentClientType,
|
|
233
|
-
target_type: TorrentClientType,
|
|
234
|
-
) -> None:
|
|
235
|
-
source_host = args.source_host or _default_host()
|
|
236
|
-
target_host = args.target_host or _default_host()
|
|
237
|
-
source_port = args.source_port or _default_port(source_type)
|
|
238
|
-
target_port = args.target_port or _default_port(target_type)
|
|
239
|
-
|
|
240
|
-
if source_type == target_type:
|
|
241
|
-
raise SystemExit("Source and target types are identical.")
|
|
242
|
-
|
|
243
|
-
same_endpoint = (
|
|
244
|
-
source_type == target_type
|
|
245
|
-
and source_host == target_host
|
|
246
|
-
and source_port == target_port
|
|
247
|
-
)
|
|
248
|
-
if same_endpoint:
|
|
249
|
-
raise SystemExit("Source and target endpoint are identical.")
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def _print_summary(stats: MigrationStats, failures: list[str], dry_run: bool) -> None:
|
|
253
|
-
mode_prefix = "DRY-RUN " if dry_run else ""
|
|
254
|
-
print(f"{mode_prefix}summary:")
|
|
255
|
-
print(f" migrated: {stats.migrated}")
|
|
256
|
-
print(f" skipped_duplicate: {stats.skipped_duplicate}")
|
|
257
|
-
print(f" failed_add: {stats.failed_add}")
|
|
258
|
-
print(f" failed_lookup: {stats.failed_lookup}")
|
|
259
|
-
print(f" failed_db_update: {stats.failed_db_update}")
|
|
260
|
-
print(f" source_removed: {stats.source_removed}")
|
|
261
|
-
if failures:
|
|
262
|
-
print("failed items:")
|
|
263
|
-
for failure in failures:
|
|
264
|
-
print(f" - {failure}")
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def run_migration(args: argparse.Namespace) -> int:
|
|
268
|
-
source_type, target_type = _parse_types_or_exit(args.source_type, args.target_type)
|
|
269
|
-
_validate_args_or_exit(args, source_type, target_type)
|
|
270
|
-
|
|
271
|
-
source_host = args.source_host or _default_host()
|
|
272
|
-
target_host = args.target_host or _default_host()
|
|
273
|
-
source_port = args.source_port or _default_port(source_type)
|
|
274
|
-
target_port = args.target_port or _default_port(target_type)
|
|
275
|
-
|
|
276
|
-
source_adapter = create_torrent_client_adapter(
|
|
277
|
-
client_type=source_type,
|
|
278
|
-
host=source_host,
|
|
279
|
-
port=source_port,
|
|
280
|
-
username=args.source_username,
|
|
281
|
-
password=args.source_password,
|
|
282
|
-
)
|
|
283
|
-
target_adapter = create_torrent_client_adapter(
|
|
284
|
-
client_type=target_type,
|
|
285
|
-
host=target_host,
|
|
286
|
-
port=target_port,
|
|
287
|
-
username=args.target_username,
|
|
288
|
-
password=args.target_password,
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
source_items = _list_source_items(source_type, source_adapter)
|
|
292
|
-
target_hash_to_id = _build_target_hash_index(target_type, target_adapter)
|
|
293
|
-
|
|
294
|
-
stats = MigrationStats()
|
|
295
|
-
failures: list[str] = []
|
|
296
|
-
had_failure = False
|
|
297
|
-
|
|
298
|
-
for item in source_items:
|
|
299
|
-
if item.hash_value in target_hash_to_id:
|
|
300
|
-
stats.skipped_duplicate += 1
|
|
301
|
-
target_id = target_hash_to_id[item.hash_value]
|
|
302
|
-
if not args.dry_run:
|
|
303
|
-
mapping_updated = _reconcile_mapping(item.source_id, target_id)
|
|
304
|
-
if not mapping_updated:
|
|
305
|
-
stats.failed_db_update += 1
|
|
306
|
-
had_failure = True
|
|
307
|
-
failures.append(
|
|
308
|
-
f"{item.name} ({item.hash_value}) duplicate mapping update failed"
|
|
309
|
-
)
|
|
310
|
-
continue
|
|
311
|
-
|
|
312
|
-
if item.payload_bytes is None and not item.magnet_link:
|
|
313
|
-
stats.failed_add += 1
|
|
314
|
-
had_failure = True
|
|
315
|
-
failures.append(
|
|
316
|
-
f"{item.name} ({item.hash_value}) no transferable payload or magnet"
|
|
317
|
-
)
|
|
318
|
-
continue
|
|
319
|
-
|
|
320
|
-
if args.dry_run:
|
|
321
|
-
print(
|
|
322
|
-
"DRY-RUN migrate:",
|
|
323
|
-
item.name,
|
|
324
|
-
item.hash_value,
|
|
325
|
-
"->",
|
|
326
|
-
target_type.value,
|
|
327
|
-
f"path={item.download_dir}",
|
|
328
|
-
)
|
|
329
|
-
continue
|
|
330
|
-
|
|
331
|
-
try:
|
|
332
|
-
new_target_id = _add_to_target(item, target_type, target_adapter)
|
|
333
|
-
except Exception as exc:
|
|
334
|
-
stats.failed_add += 1
|
|
335
|
-
had_failure = True
|
|
336
|
-
failures.append(f"{item.name} ({item.hash_value}) add failed: {exc}")
|
|
337
|
-
continue
|
|
338
|
-
|
|
339
|
-
if new_target_id is None:
|
|
340
|
-
stats.failed_lookup += 1
|
|
341
|
-
had_failure = True
|
|
342
|
-
failures.append(
|
|
343
|
-
f"{item.name} ({item.hash_value}) added but target id lookup failed"
|
|
344
|
-
)
|
|
345
|
-
continue
|
|
346
|
-
|
|
347
|
-
target_hash_to_id[item.hash_value] = new_target_id
|
|
348
|
-
stats.migrated += 1
|
|
349
|
-
|
|
350
|
-
if not _reconcile_mapping(item.source_id, new_target_id):
|
|
351
|
-
stats.failed_db_update += 1
|
|
352
|
-
had_failure = True
|
|
353
|
-
failures.append(
|
|
354
|
-
f"{item.name} ({item.hash_value}) mapping update failed "
|
|
355
|
-
f"{item.source_id}->{new_target_id}"
|
|
356
|
-
)
|
|
357
|
-
_print_summary(stats, failures, args.dry_run)
|
|
358
|
-
return 1 if had_failure else 0
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
def main() -> None:
|
|
362
|
-
parser = _build_parser()
|
|
363
|
-
args = parser.parse_args()
|
|
364
|
-
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
|
|
365
|
-
try:
|
|
366
|
-
exit_code = run_migration(args)
|
|
367
|
-
except SystemExit as exc:
|
|
368
|
-
if isinstance(exc.code, int):
|
|
369
|
-
raise
|
|
370
|
-
parser.error(str(exc))
|
|
371
|
-
except Exception as exc:
|
|
372
|
-
print(f"Migration failed: {exc}")
|
|
373
|
-
raise SystemExit(1) from exc
|
|
374
|
-
raise SystemExit(exit_code)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|