This is not so easy to do in SQL, since it is order-dependent, which SQL is not well suited to.
The query is quite unwieldy, so I'll give it in full first, followed by a breakdown showing how it's put together.
SELECT @rownum:=@rownum+1 AS id, t.user_id, type, date, urls FROM
(SELECT MIN(ID) AS original_id, user_id, type, date, GROUP_CONCAT(url) urls FROM
(SELECT i1.*,
IF(i1.type='image',
IFNULL((SELECT MIN(i2.ID)-1 FROM Items i2
WHERE i2.ID>i1.ID AND
(i2.type!=i1.type OR i1.user_id!=i2.user_id OR i1.date!=i2.date)),
(SELECT MAX(id) FROM Items)),
i1.ID) AS lastRow,
IF (i1.type='image',
IFNULL(SELECT MAX(i3.ID)+1 FROM Items i3
WHERE i3.ID<=i1.ID AND
(i3.type!=i1.type OR i1.user_id!=i3.user_id OR i1.date!=i3.date)),
(SELECT MIN(id) FROM Items)),
i1.ID) AS firstRow) AS groupItems
GROUP BY user_id, type, date, firstRow, lastRow) t, (SELECT @rownum:=0) r
ORDER BY t.original_id;
The query uses a correlated subquery to find the start and end IDs of each image group. The group boundary is an item that is not the same type, user or date.
SELECT i1.ID,
IF(i1.type='image',
IFNULL((SELECT MIN(i2.ID)-1 FROM Items i2
WHERE i2.ID>i1.ID AND
(i2.type!=i1.type OR i1.user_id!=i2.user_id OR i1.date!=i2.date)),
(SELECT MAX(id) FROM Items)),
i1.ID) AS lastRow,
IF (i1.type='image',
IFNULL(SELECT MAX(i3.ID)+1 FROM Items i3
WHERE i3.ID<=i1.ID AND
(i3.type!=i1.type OR i1.user_id!=i3.user_id OR i1.date!=i3.date)),
(SELECT MIN(id) FROM Items)),
i1.ID) AS firstRow
For each item, the firstRow/lastRow columns give the start and end of the group. We can then use GROUP_CONCAT to concatenate all the URLs. To preserve order, MIN(id) is output, giving the first ID of each group.
SELECT MIN(id) AS original_id, user_id, type, date, GROUP_CONCAT(url) urls FROM
(SELECT i1.*,
IF(i1.type='image',
IFNULL((SELECT MIN(i2.ID)-1 FROM Items i2
WHERE i2.ID>i1.ID AND
(i2.type!=i1.type OR i1.user_id!=i2.user_id OR i1.date!=i2.date)),
(SELECT MAX(id) FROM Items)),
i1.ID) AS lastRow,
IF (i1.type='image',
IFNULL(SELECT MAX(i3.ID)+1 FROM Items i3
WHERE i3.ID<=i1.ID AND
(i3.type!=i1.type OR i1.user_id!=i3.user_id OR i1.date!=i3.date)),
(SELECT MIN(id) FROM Items)),
i1.ID) AS firstRow) AS groupItems
GROUP BY user_id, type, date, firstRow, lastRow
Finally, to get consecutive IDs for the new table, use variable to compute the rank:
SELECT @rownum:=@rownum+1 AS id, user_id, type, date, urls FROM
(SELECT MIN(ID) AS original_id, user_id, type, date, GROUP_CONCAT(url) urls FROM
(SELECT i1.*,
IF(i1.type='image',
IFNULL((SELECT MIN(i2.ID)-1 FROM Items i2
WHERE i2.ID>i1.ID AND
(i2.type!=i1.type OR i1.user_id!=i2.user_id OR i1.date!=i2.date)),
(SELECT MAX(id) FROM Items)),
i1.ID) AS lastRow,
IF (i1.type='image',
IFNULL(SELECT MAX(i3.ID)+1 FROM Items i3
WHERE i3.ID<=i1.ID AND
(i3.type!=i1.type OR i1.user_id!=i3.user_id OR i1.date!=i3.date)),
(SELECT MIN(id) FROM Items)),
i1.ID) AS firstRow) AS groupItems
GROUP BY user_id, type, date, firstRow, lastRow) t, (SELECT @rownum:=0) r
ORDER BY t.original_id;
SQL is best suited to working with unordered sets of data, rather than sequences, as here.
If you can do this in the presentation code, or possibly better in your application layer, I imagine that will be faster and more flexible. A hand-coded solution would find the start and end of each group in a single pass through the data. I doubt the SQL query will execute as efficiently as that.